From e4281934f764a16dadf9f9cee94b139529be8354 Mon Sep 17 00:00:00 2001 From: Brad Edwards Date: Sun, 27 Sep 2026 02:23:33 +0200 Subject: [PATCH 01/11] feat: enforce the administrative-only runtime API trust boundary --- ...59-runtime-api-trust-boundary-preflight.md | 189 +++++ docs/explain/sdl/runtime-architecture.md | 9 + docs/public/guides/control-plane.md | 130 ++++ docs/public/index.md | 4 +- docs/requirements/API-404/requirement.md | 20 +- .../raes_contracts/operation_lifecycle.py | 11 +- .../control_plane_api/__init__.py | 8 +- .../raes_runtime/control_plane_api/_auth.py | 121 ++- .../control_plane_api/_health_routes.py | 5 +- .../control_plane_api/_operation_routes.py | 26 +- .../control_plane_api/_participant_routes.py | 16 +- .../control_plane_api/_workflow_routes.py | 6 +- .../raes_runtime/control_plane_api_guards.py | 32 + ...control_plane_api_participant_retrieval.py | 23 +- .../raes_runtime/control_plane_security.py | 95 ++- ...t_issue_1359_runtime_api_trust_boundary.py | 729 ++++++++++++++++++ .../tests/test_runtime_control_plane_api.py | 3 +- 17 files changed, 1345 insertions(+), 82 deletions(-) create mode 100644 docs/decisions/issue-1359-runtime-api-trust-boundary-preflight.md create mode 100644 docs/public/guides/control-plane.md create mode 100644 implementations/python/tests/test_issue_1359_runtime_api_trust_boundary.py 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/explain/sdl/runtime-architecture.md b/docs/explain/sdl/runtime-architecture.md index aeae0de6f..825003adf 100644 --- a/docs/explain/sdl/runtime-architecture.md +++ b/docs/explain/sdl/runtime-architecture.md @@ -673,6 +673,15 @@ 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: diff --git a/docs/public/guides/control-plane.md b/docs/public/guides/control-plane.md new file mode 100644 index 000000000..399ec8fbc --- /dev/null +++ b/docs/public/guides/control-plane.md @@ -0,0 +1,130 @@ +# 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. + +| 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 `:`. +An unknown bearer token fails with `401`. It never falls back to proxy headers. + +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 + +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/requirements/API-404/requirement.md b/docs/requirements/API-404/requirement.md index 883074d79..59e35dba3 100644 --- a/docs/requirements/API-404/requirement.md +++ b/docs/requirements/API-404/requirement.md @@ -6,7 +6,7 @@ type: FUNCTIONAL priority: MUST wave: 1 created_at: 2026-04-03T05:55:58.825305Z -updated_at: 2026-09-23T00:00:00.000000Z +updated_at: 2026-09-26T00:00:00.000000Z --- # API-404 — Secure, Durable, And Idempotent Control-Plane Semantics @@ -67,6 +67,12 @@ selected P1 core. The corresponding runtime guarantee identifiers are `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, administrative read, administrative mutation +or operator resolution. P2 composition shall fail when any other route is +registered. Every P2 response, including errors and idempotent readback, shall +carry `Cache-Control: no-store`. + 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 @@ -131,6 +137,18 @@ identifies those implementation gaps and the retained canonical requirements. ## Traceability +- 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, unambiguous subject bindings and fail-closed configured-principal shape and bound validation) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/operation_lifecycle.py` (Operation-context bounds reused by configured-principal validation) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_api/_auth.py` (One declared transport authority per served route and fail-closed route inventory) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_api/__init__.py` (Route-authority inventory enforced at app construction 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) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_api/_operation_routes.py` (No-store installation and uncacheable redacted 500 envelope) +- 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, per-route admission, governed and legacy views, replay scope, no-store and principal-shape checks) + - 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) diff --git a/implementations/python/packages/raes_contracts/operation_lifecycle.py b/implementations/python/packages/raes_contracts/operation_lifecycle.py index 2a76e16e4..2f6b96b93 100644 --- a/implementations/python/packages/raes_contracts/operation_lifecycle.py +++ b/implementations/python/packages/raes_contracts/operation_lifecycle.py @@ -37,7 +37,9 @@ class OperationKind(str, Enum): INDETERMINATE_RESOLUTION = "indeterminate-resolution" -_ContextString = Annotated[str, Field(min_length=1, max_length=256)] +OPERATION_CONTEXT_STRING_MAX_LENGTH = 256 +OPERATION_AUTHORIZATION_SCOPE_MAX_ENTRIES = 64 +_ContextString = Annotated[str, Field(min_length=1, max_length=OPERATION_CONTEXT_STRING_MAX_LENGTH)] _RequestCommitment = Annotated[str, Field(pattern=r"^sha256:[a-f0-9]{64}$")] @@ -47,7 +49,10 @@ class OperationAdmissionContext(ContractModel): model_config = ConfigDict(extra="forbid", frozen=True) actor_id: _ContextString - authorization_scope: tuple[_ContextString, ...] = Field(min_length=1, max_length=64) + authorization_scope: tuple[_ContextString, ...] = Field( + min_length=1, + max_length=OPERATION_AUTHORIZATION_SCOPE_MAX_ENTRIES, + ) target_scope: _ContextString run_scope: _ContextString operation_kind: OperationKind @@ -165,6 +170,8 @@ def require_operation_terminal_diagnostics( __all__ = ( "LEGAL_OPERATION_TRANSITIONS", + "OPERATION_AUTHORIZATION_SCOPE_MAX_ENTRIES", + "OPERATION_CONTEXT_STRING_MAX_LENGTH", "OperationAdmissionContext", "OperationKind", "OperationState", 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 8590828ad..f2b2c00e7 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. @@ -38,7 +41,7 @@ ) from ..control_plane_security import ControlPlaneSecurityConfig from ..control_plane_store_lease import require_single_worker_configuration -from ._auth import _ControlPlaneApiAuth +from ._auth import _ControlPlaneApiAuth, _require_route_transport_authority from ._health_routes import _register_health_routes from ._offload import _ControlPlaneCallExecutor from ._operation_routes import _install_request_guards, _register_operation_routes @@ -124,4 +127,5 @@ async def lifespan(_app: FastAPI) -> AsyncIterator[None]: _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) 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 48486fb58..8ec020ec8 100644 --- a/implementations/python/packages/raes_runtime/control_plane_api/_auth.py +++ b/implementations/python/packages/raes_runtime/control_plane_api/_auth.py @@ -1,17 +1,30 @@ -"""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_ROLES, ControlPlaneIdentity, ControlPlaneRole, + ControlPlaneRouteAuthority, ControlPlaneSecurityConfig, ) @@ -27,33 +40,11 @@ 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, - ) - - def resolution_identity(self, request: Request) -> ControlPlaneIdentity: - identity = self._authenticated_identity(request) - return self._authorize( - identity, - roles={ControlPlaneRole.OPERATOR}, - request=request, - ) + return self._authorize(identity, roles=ROUTE_AUTHORITY_ROLES[authority], request=request) def _authenticated_identity(self, request: Request) -> ControlPlaneIdentity: try: @@ -109,7 +100,7 @@ def _authorize( self, identity: ControlPlaneIdentity, *, - roles: set[ControlPlaneRole], + roles: frozenset[ControlPlaneRole], request: Request, ) -> ControlPlaneIdentity: if not identity.roles.isdisjoint(roles): @@ -140,18 +131,74 @@ def _record_audit_safely(self, **fields: object) -> None: _LOGGER.error("control-plane-auth-audit-failed") -def _mutating_identity_dependency(request: Request) -> ControlPlaneIdentity: - return request.app.state.control_plane_api_auth.mutating_identity(request) +def _public_probe_dependency() -> None: + """Declare a value-free public probe; it admits no identity.""" -def _read_identity_dependency(request: Request) -> ControlPlaneIdentity: - return request.app.state.control_plane_api_auth.read_identity(request) +def _administrative_read_dependency(request: Request) -> ControlPlaneIdentity: + return request.app.state.control_plane_api_auth.admit(request, ControlPlaneRouteAuthority.ADMINISTRATIVE_READ) -def _resolution_identity_dependency(request: Request) -> ControlPlaneIdentity: - return request.app.state.control_plane_api_auth.resolution_identity(request) +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, + } +) -_MutatingIdentity = Annotated[ControlPlaneIdentity, Depends(_mutating_identity_dependency)] -_ReadIdentity = Annotated[ControlPlaneIdentity, Depends(_read_identity_dependency)] -_ResolutionIdentity = Annotated[ControlPlaneIdentity, Depends(_resolution_identity_dependency)] +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 authority is ControlPlaneRouteAuthority.PUBLIC_PROBE and route.methods != {"GET"}: + raise ValueError(f"public-probe transport authority is GET-only: {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, so a new operation, inject, event or export route + cannot silently bypass P2 admission. + """ + + 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) 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 index 7001063b7..9a223b2e9 100644 --- a/implementations/python/packages/raes_runtime/control_plane_api/_health_routes.py +++ b/implementations/python/packages/raes_runtime/control_plane_api/_health_routes.py @@ -7,14 +7,15 @@ 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") + @app.get("/health/live", dependencies=[_PublicProbe]) async def liveness() -> JSONResponse: return JSONResponse(content=control_plane_liveness().to_payload()) - @app.get("/health/ready") + @app.get("/health/ready", dependencies=[_PublicProbe]) async def readiness() -> JSONResponse: health = control_plane_readiness(control_plane) return JSONResponse( 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 1aa675854..814685bb6 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 @@ -18,7 +18,7 @@ ) from ..control_plane import RuntimeControlPlane -from ..control_plane_api_guards import RequestSizeLimitMiddleware +from ..control_plane_api_guards import NO_STORE_CACHE_CONTROL, NoStoreResponseMiddleware, RequestSizeLimitMiddleware from ..control_plane_api_models import ( _evaluation_plan, _operation_status_model, @@ -28,7 +28,7 @@ ) from ..control_plane_recovery import IndeterminateResolutionDisposition from ..control_plane_security import ControlPlaneSecurityConfig -from ._auth import _MutatingIdentity, _ReadIdentity, _ResolutionIdentity +from ._auth import _AdministrativeMutationIdentity, _AdministrativeReadIdentity, _OperatorResolutionIdentity from ._offload import _control_plane_calls from ._responses import ( _CONFLICT_RESPONSES, @@ -89,6 +89,8 @@ def _install_request_guards( 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: @@ -96,7 +98,11 @@ async def _redacted_errors(request: Request, exc: Exception) -> JSONResponse: 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"}) + return JSONResponse( + status_code=500, + content={"detail": "internal server error"}, + headers={"cache-control": NO_STORE_CACHE_CONTROL}, + ) @app.exception_handler(RequestValidationError) async def _redacted_request_validation_errors(request: Request, exc: RequestValidationError) -> JSONResponse: @@ -134,7 +140,7 @@ async def resolve_indeterminate_operation( operation_id: str, request: Request, resolution: _IndeterminateResolutionRequest, - identity: _ResolutionIdentity, + identity: _OperatorResolutionIdentity, ) -> OperationReceiptModel: try: receipt = await _control_plane_calls(request).mutate( @@ -161,7 +167,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) @@ -198,7 +204,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) @@ -234,7 +240,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) @@ -270,7 +276,7 @@ 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, identity=identity) @@ -290,7 +296,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( @@ -311,7 +317,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( 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 c3d2134a0..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,7 +19,7 @@ _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, @@ -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) @@ -71,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: @@ -112,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: @@ -152,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() @@ -176,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() @@ -206,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() @@ -231,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() 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 55bc4e831..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,7 +6,7 @@ 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, _conflict_detail, _receipt_response @@ -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() @@ -40,7 +40,7 @@ async def cancel_workflow( @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: 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 9d9fb6b8d..6ef3e973f 100644 --- a/implementations/python/packages/raes_runtime/control_plane_api_guards.py +++ b/implementations/python/packages/raes_runtime/control_plane_api_guards.py @@ -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__) @@ -56,6 +57,37 @@ 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) + + class RequestSizeLimitMiddleware: """Reject oversized HTTP bodies before FastAPI parses or dispatches them. 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 91f97bee6..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 @@ -124,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, @@ -153,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, @@ -182,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( diff --git a/implementations/python/packages/raes_runtime/control_plane_security.py b/implementations/python/packages/raes_runtime/control_plane_security.py index 23d37f1c3..17276fc9e 100644 --- a/implementations/python/packages/raes_runtime/control_plane_security.py +++ b/implementations/python/packages/raes_runtime/control_plane_security.py @@ -8,6 +8,13 @@ from enum import Enum from types import MappingProxyType +from raes_contracts.operation_lifecycle import ( + OPERATION_AUTHORIZATION_SCOPE_MAX_ENTRIES, + OPERATION_CONTEXT_STRING_MAX_LENGTH, +) + +from .control_plane_operation_context import operation_actor_scope + class ControlPlaneRole(str, Enum): """Authorization roles for control-plane callers.""" @@ -17,6 +24,56 @@ 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. +""" + + +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.""" @@ -25,8 +82,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) @@ -37,8 +93,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) @@ -61,6 +116,37 @@ def _require_identity_headers(verified: str, identity: str) -> None: raise ValueError("identity headers must be distinct and must not alias Authorization") +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) + if len(actor) > OPERATION_CONTEXT_STRING_MAX_LENGTH: + raise ValueError("configured principal identity exceeds the operation-context bound") + if len(authorization_scope) > OPERATION_AUTHORIZATION_SCOPE_MAX_ENTRIES or any( + len(entry) > OPERATION_CONTEXT_STRING_MAX_LENGTH for entry in authorization_scope + ): + raise ValueError("configured principal authorization scope exceeds the operation-context bound") + + def _frozen_principals(principals: Mapping[str, ControlPlaneIdentity]) -> Mapping[str, ControlPlaneIdentity]: copied = dict(principals) for key, principal in copied.items(): @@ -72,6 +158,7 @@ def _frozen_principals(principals: Mapping[str, ControlPlaneIdentity]) -> Mappin or not principal.identity.strip() ): raise ValueError("configured principal must have a non-empty identity") + _require_principal_shape(principal) return MappingProxyType(copied) 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..122381d31 --- /dev/null +++ b/implementations/python/tests/test_issue_1359_runtime_api_trust_boundary.py @@ -0,0 +1,729 @@ +"""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 ( + _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 _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(_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)) + + with pytest.raises(ValueError, match="transport authority"): + create_control_plane_app(RuntimeControlPlane(create_stub_target())) + + +# --- 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(("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": "identity is not authorized for this target"} + + +@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" + + with pytest.raises(ValueError, match="configured principal") as caught: + ControlPlaneSecurityConfig(**{mapping: {secret: _MALFORMED_PRINCIPALS[principal]()}}) + + 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_runtime_control_plane_api.py b/implementations/python/tests/test_runtime_control_plane_api.py index 11eec0e06..81c4a32cc 100644 --- a/implementations/python/tests/test_runtime_control_plane_api.py +++ b/implementations/python/tests/test_runtime_control_plane_api.py @@ -55,6 +55,7 @@ from raes_runtime.control_plane_security import ( ControlPlaneIdentity, ControlPlaneRole, + ControlPlaneRouteAuthority, ControlPlaneSecurityConfig, ) from raes_runtime.control_plane_store import ( @@ -1721,7 +1722,7 @@ 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" From 352c3ed2d48434544092ba6e50b6288d5f020161 Mon Sep 17 00:00:00 2001 From: Brad Edwards Date: Sun, 27 Sep 2026 02:33:40 +0200 Subject: [PATCH 02/11] fix: keep contract models unchanged and republish research evidence captures --- docs/requirements/API-404/requirement.md | 1 - .../analysis-v53.json | 154 ++++ .../bundles/retest-v53.json | 122 +++ .../execution-snapshot-v53.json | 652 ++++++++++++++++ .../formal-semantic-validation/index.md | 7 +- .../specification-coverage/analysis-v53.json | 104 +++ ...specification-coverage-issue-1360-v53.json | 10 + .../execution-snapshot-v53.json | 700 ++++++++++++++++++ docs/research/specification-coverage/index.md | 7 +- .../raes_contracts/operation_lifecycle.py | 11 +- .../raes_runtime/control_plane_security.py | 27 +- .../tests/test_formal_semantic_validation.py | 5 +- .../test_issue_989_versioned_evidence.py | 6 +- .../tests/test_specification_coverage.py | 2 +- tools/check_specification_coverage.py | 5 +- tools/formal_semantic_validation/_baseline.py | 14 +- tools/formal_semantic_validation/_loading.py | 5 +- .../_release_revisions.py | 3 +- tools/formal_semantic_validation/_releases.py | 6 +- tools/formal_semantic_validation/_retest.py | 2 +- 20 files changed, 1807 insertions(+), 36 deletions(-) create mode 100644 docs/research/formal-semantic-validation/analysis-v53.json create mode 100644 docs/research/formal-semantic-validation/bundles/retest-v53.json create mode 100644 docs/research/formal-semantic-validation/execution-snapshot-v53.json create mode 100644 docs/research/specification-coverage/analysis-v53.json create mode 100644 docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1360-v53.json create mode 100644 docs/research/specification-coverage/execution-snapshot-v53.json diff --git a/docs/requirements/API-404/requirement.md b/docs/requirements/API-404/requirement.md index 59e35dba3..8ce9e507b 100644 --- a/docs/requirements/API-404/requirement.md +++ b/docs/requirements/API-404/requirement.md @@ -140,7 +140,6 @@ identifies those implementation gaps and the retained canonical requirements. - 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, unambiguous subject bindings and fail-closed configured-principal shape and bound validation) -- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/operation_lifecycle.py` (Operation-context bounds reused by configured-principal validation) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_api/_auth.py` (One declared transport authority per served route and fail-closed route inventory) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_api/__init__.py` (Route-authority inventory enforced at app construction 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) 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..c5f3d66a4 --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v53.json @@ -0,0 +1,154 @@ +{ + "analysis_id": "issue-1360-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-1360-execution-v53", + "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 runtime trust-boundary source. Prior claim limits and unsupported classes remain unchanged.", + "protocol_revision": "2.0.0" +} 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..83972aed3 --- /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": "a05cdeb28f892f732401436804a5fe4ecd339b3772a33e07003cd953b81f937f", + "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": "d7293bfbf79b1e0b030b58071e2e376a5e2e503af6fde01d35da877392b69566" +} 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..761568602 --- /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-27T00:30:42.467731+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-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-1360-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-1360-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-1360-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-1360-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-1360-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-1360-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-1360-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-1360-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-1360-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-1360-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-1360-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-1360-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-1360-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-1360-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-1360-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-1360-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-1360-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-1360-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-1360-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-1360-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-1360-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-1360-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-1360-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-1360-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-1360-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-1360-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-1360-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": "998364710820e96a11d9ca9ba153ab5ef1cf8633", + "source_state": { + "base_revision": "998364710820e96a11d9ca9ba153ab5ef1cf8633", + "checkout_state": "modified", + "implementation_digest": "a2bc504324212209f545f9677555d4800906f0ea0496745aac0386b65b956ffc", + "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/index.md b/docs/research/formal-semantic-validation/index.md index 7990e3b9f..2e08e198f 100644 --- a/docs/research/formal-semantic-validation/index.md +++ b/docs/research/formal-semantic-validation/index.md @@ -336,7 +336,7 @@ 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 53.0.0, rejects unsupported future +Current validation requires explicit release 54.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 @@ -494,3 +494,8 @@ 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 controls on the source that adds issue #1359 +runtime API trust-boundary enforcement in +[`execution-snapshot-v53.json`](execution-snapshot-v53.json) and +[`analysis-v53.json`](analysis-v53.json). Earlier captures retain their source identities. diff --git a/docs/research/specification-coverage/analysis-v53.json b/docs/research/specification-coverage/analysis-v53.json new file mode 100644 index 000000000..1d33a931a --- /dev/null +++ b/docs/research/specification-coverage/analysis-v53.json @@ -0,0 +1,104 @@ +{ + "analysis_id": "issue-1360-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-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.", + "This capture replays the merged issue #1360 and #1359 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-v53", + "snapshot_sha256": "830254e89bd2601e6fcb25a782706041dc4591490499617f6f7eda43f20173a9" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1360-v53.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1360-v53.json new file mode 100644 index 000000000..fc9fbdbe8 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1360-v53.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v53.json", + "analysis_sha256": "73d07e1040d76e4a520f486c3efc3f3cd348729713d5a37ff7da3851256fec69", + "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": "06975d0dc9559ed8f3eea378d97822e4a866289c70144ab355b31d3752fc73d7" +} 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..9e0556c31 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v53.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-27T00:30:42.467731+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": "998364710820e96a11d9ca9ba153ab5ef1cf8633", + "snapshot_id": "issue-1360-specification-coverage-v53", + "snapshot_revision": "53.0.0", + "source_state": { + "base_revision": "998364710820e96a11d9ca9ba153ab5ef1cf8633", + "checkout_state": "modified", + "implementation_digest": "a2bc504324212209f545f9677555d4800906f0ea0496745aac0386b65b956ffc", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/index.md b/docs/research/specification-coverage/index.md index 06740a7a3..298a23e8c 100644 --- a/docs/research/specification-coverage/index.md +++ b/docs/research/specification-coverage/index.md @@ -292,7 +292,7 @@ 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 52.0.0 and rejects duplicate or unsupported +Current validation requires release 53.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; @@ -430,3 +430,8 @@ 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 protocol on the source that adds issue #1359 +runtime API trust-boundary enforcement in +[`execution-snapshot-v53.json`](execution-snapshot-v53.json) and +[`analysis-v53.json`](analysis-v53.json). Earlier captures retain their source identities. diff --git a/implementations/python/packages/raes_contracts/operation_lifecycle.py b/implementations/python/packages/raes_contracts/operation_lifecycle.py index 2f6b96b93..2a76e16e4 100644 --- a/implementations/python/packages/raes_contracts/operation_lifecycle.py +++ b/implementations/python/packages/raes_contracts/operation_lifecycle.py @@ -37,9 +37,7 @@ class OperationKind(str, Enum): INDETERMINATE_RESOLUTION = "indeterminate-resolution" -OPERATION_CONTEXT_STRING_MAX_LENGTH = 256 -OPERATION_AUTHORIZATION_SCOPE_MAX_ENTRIES = 64 -_ContextString = Annotated[str, Field(min_length=1, max_length=OPERATION_CONTEXT_STRING_MAX_LENGTH)] +_ContextString = Annotated[str, Field(min_length=1, max_length=256)] _RequestCommitment = Annotated[str, Field(pattern=r"^sha256:[a-f0-9]{64}$")] @@ -49,10 +47,7 @@ class OperationAdmissionContext(ContractModel): model_config = ConfigDict(extra="forbid", frozen=True) actor_id: _ContextString - authorization_scope: tuple[_ContextString, ...] = Field( - min_length=1, - max_length=OPERATION_AUTHORIZATION_SCOPE_MAX_ENTRIES, - ) + authorization_scope: tuple[_ContextString, ...] = Field(min_length=1, max_length=64) target_scope: _ContextString run_scope: _ContextString operation_kind: OperationKind @@ -170,8 +165,6 @@ def require_operation_terminal_diagnostics( __all__ = ( "LEGAL_OPERATION_TRANSITIONS", - "OPERATION_AUTHORIZATION_SCOPE_MAX_ENTRIES", - "OPERATION_CONTEXT_STRING_MAX_LENGTH", "OperationAdmissionContext", "OperationKind", "OperationState", diff --git a/implementations/python/packages/raes_runtime/control_plane_security.py b/implementations/python/packages/raes_runtime/control_plane_security.py index 17276fc9e..ff7d787e3 100644 --- a/implementations/python/packages/raes_runtime/control_plane_security.py +++ b/implementations/python/packages/raes_runtime/control_plane_security.py @@ -8,10 +8,8 @@ from enum import Enum from types import MappingProxyType -from raes_contracts.operation_lifecycle import ( - OPERATION_AUTHORIZATION_SCOPE_MAX_ENTRIES, - OPERATION_CONTEXT_STRING_MAX_LENGTH, -) +from pydantic import TypeAdapter, ValidationError +from raes_contracts.operation_lifecycle import OperationAdmissionContext from .control_plane_operation_context import operation_actor_scope @@ -116,6 +114,13 @@ def _require_identity_headers(verified: str, identity: str) -> None: 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. @@ -139,12 +144,14 @@ def _require_principal_shape(principal: ControlPlaneIdentity) -> None: # 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) - if len(actor) > OPERATION_CONTEXT_STRING_MAX_LENGTH: - raise ValueError("configured principal identity exceeds the operation-context bound") - if len(authorization_scope) > OPERATION_AUTHORIZATION_SCOPE_MAX_ENTRIES or any( - len(entry) > OPERATION_CONTEXT_STRING_MAX_LENGTH for entry in authorization_scope - ): - raise ValueError("configured principal authorization scope exceeds the operation-context bound") + 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]: diff --git a/implementations/python/tests/test_formal_semantic_validation.py b/implementations/python/tests/test_formal_semantic_validation.py index d84008986..4ed35519d 100644 --- a/implementations/python/tests/test_formal_semantic_validation.py +++ b/implementations/python/tests/test_formal_semantic_validation.py @@ -132,6 +132,7 @@ def test_atomic_release_index_validates_every_historical_bundle() -> None: "51.0.0", "52.0.0", "53.0.0", + "54.0.0", ] assert all(validate_release_bundle(REPO_ROOT, release) == [] for release in releases) @@ -140,10 +141,10 @@ 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"] == "53.0.0" + assert release.manifest["revision"] == "54.0.0" assert protocol["revision"] == "2.0.0" assert corpus["revision"] == "4.0.0" - assert snapshot["baseline"]["release_revision"] == "52.0.0" + assert snapshot["baseline"]["release_revision"] == "53.0.0" assert snapshot["deviations"] == [] assert validate_retest_bundle(REPO_ROOT, release, protocol, corpus, snapshot, analysis) == [] diff --git a/implementations/python/tests/test_issue_989_versioned_evidence.py b/implementations/python/tests/test_issue_989_versioned_evidence.py index 3a6f23105..d7cb91dca 100644 --- a/implementations/python/tests/test_issue_989_versioned_evidence.py +++ b/implementations/python/tests/test_issue_989_versioned_evidence.py @@ -230,7 +230,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"] == "53.0.0" + assert release.manifest["revision"] == "54.0.0" original = _retest.replay_case def changed_result(root, case): @@ -283,7 +283,7 @@ def test_specification_current_capture_does_not_accept_old_artifact_digest(artif from tools.check_specification_coverage import load_bundle, validate_bundle manifest, protocol, snapshot, analysis = copy_bundle(load_bundle, ROOT) - assert manifest["revision"] == "52.0.0" + assert manifest["revision"] == "53.0.0" snapshot = deepcopy(snapshot) artifact = next(a for a in snapshot["artifacts"] if a["artifact_id"] == artifact_id) artifact["sha256"] = old_digest @@ -505,6 +505,7 @@ def test_no_capture_can_be_silently_dropped(monkeypatch, family, removed): "51.0.0", "52.0.0", "53.0.0", + "54.0.0", ] if family == "formal" else [ @@ -561,6 +562,7 @@ def test_no_capture_can_be_silently_dropped(monkeypatch, family, removed): "50.0.0", "51.0.0", "52.0.0", + "53.0.0", ] ) revisions.pop(-1 if removed == "current" else 0) diff --git a/implementations/python/tests/test_specification_coverage.py b/implementations/python/tests/test_specification_coverage.py index d150999d3..d79b41ffa 100644 --- a/implementations/python/tests/test_specification_coverage.py +++ b/implementations/python/tests/test_specification_coverage.py @@ -53,7 +53,7 @@ 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", "19.0.0"} manifest, *_rest = copy_bundle(load_bundle, REPO_ROOT) - assert manifest["revision"] == "52.0.0" + assert manifest["revision"] == "53.0.0" def test_historical_failures_name_the_revision_specific_documents() -> None: diff --git a/tools/check_specification_coverage.py b/tools/check_specification_coverage.py index 86799ea70..4749299ec 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") != "52.0.0" or {record.get("revision") for _, record in records} != { + if dict(records)[current_path].get("revision") != "53.0.0" or {record.get("revision") for _, record in records} != { "1.0.0", "1.1.0", "2.0.0", @@ -156,8 +156,9 @@ def _load_bundle_index(repo_root: Path) -> list[tuple[str, dict[str, object]]]: "50.0.0", "51.0.0", "52.0.0", + "53.0.0", }: - raise ValueError("coverage evidence requires the explicit current 52.0.0 release and supported history") + raise ValueError("coverage evidence requires the explicit current 53.0.0 release and supported history") return records diff --git a/tools/formal_semantic_validation/_baseline.py b/tools/formal_semantic_validation/_baseline.py index dd252599e..6ea0a16dc 100644 --- a/tools/formal_semantic_validation/_baseline.py +++ b/tools/formal_semantic_validation/_baseline.py @@ -82,6 +82,7 @@ "50.0.0", "51.0.0", "52.0.0", + "53.0.0", } ) _V3_CORPUS_REVISIONS = frozenset( @@ -230,7 +231,18 @@ def _selected_baseline_manifest( if baseline_revision in _V2_REVISIONS else "docs/research/formal-semantic-validation/protocol-v1.json" ) - 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"}: + 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", + }: 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" diff --git a/tools/formal_semantic_validation/_loading.py b/tools/formal_semantic_validation/_loading.py index 112bad274..882f989b2 100644 --- a/tools/formal_semantic_validation/_loading.py +++ b/tools/formal_semantic_validation/_loading.py @@ -83,6 +83,7 @@ def load_release_bundles(repo_root: Path = REPO_ROOT) -> list[EvidenceRelease]: "51.0.0", "52.0.0", "53.0.0", + "54.0.0", }: raise ValueError("formal evidence requires every supported historical and current release") releases: list[EvidenceRelease] = [] @@ -129,6 +130,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") != "53.0.0" or release.protocol.get("revision") != "2.0.0": - raise ValueError("the current formal evidence release must be the explicit 53.0.0 retest") + if release.manifest.get("revision") != "54.0.0" or release.protocol.get("revision") != "2.0.0": + raise ValueError("the current formal evidence release must be the explicit 54.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 index ed9d002d8..60dd00488 100644 --- a/tools/formal_semantic_validation/_release_revisions.py +++ b/tools/formal_semantic_validation/_release_revisions.py @@ -52,8 +52,9 @@ "50.0.0", "51.0.0", "52.0.0", + "53.0.0", } ) -_SUPPORTED_RETEST_REVISIONS = _HISTORICAL_RETEST_REVISIONS | {"53.0.0"} +_SUPPORTED_RETEST_REVISIONS = _HISTORICAL_RETEST_REVISIONS | {"54.0.0"} _SOURCE_BOUND_RETEST_REVISIONS = _SUPPORTED_RETEST_REVISIONS - {"3.0.0"} diff --git a/tools/formal_semantic_validation/_releases.py b/tools/formal_semantic_validation/_releases.py index 6512c6614..8a5dd11fc 100644 --- a/tools/formal_semantic_validation/_releases.py +++ b/tools/formal_semantic_validation/_releases.py @@ -159,7 +159,7 @@ def validate_release_bundle(repo_root: Path, release: EvidenceRelease) -> list[P release.corpus, release.snapshot, release.analysis, - replay_current=manifest.get("revision") == "53.0.0", + replay_current=manifest.get("revision") == "54.0.0", ) ) else: @@ -270,6 +270,7 @@ def _expected_corpus_revision(release_revision: object) -> str: "51.0.0", "52.0.0", "53.0.0", + "54.0.0", }: expected_corpus_revision = "4.0.0" return expected_corpus_revision @@ -297,7 +298,7 @@ def validate_retest_bundle( return [ _failure( "formal-validation-current-replay-required", - "only releases 3.0.0 through 52.0.0 can use integrated historical validation", + "only releases 3.0.0 through 53.0.0 can use integrated historical validation", snapshot_path, ) ] @@ -423,6 +424,7 @@ def _current_retest_source_failures( "51.0.0": "50.0.0", "52.0.0": "51.0.0", "53.0.0": "52.0.0", + "54.0.0": "53.0.0", }[release_revision] if not isinstance(baseline, Mapping) or baseline.get("release_revision") != expected_baseline: failures.append( diff --git a/tools/formal_semantic_validation/_retest.py b/tools/formal_semantic_validation/_retest.py index d21b546ff..aa91752de 100644 --- a/tools/formal_semantic_validation/_retest.py +++ b/tools/formal_semantic_validation/_retest.py @@ -37,7 +37,7 @@ ) from tools.policy.common import PolicyFailure -_SOURCE_STATE_REVISIONS = frozenset(f"{revision}.0.0" for revision in range(4, 54)) +_SOURCE_STATE_REVISIONS = frozenset(f"{revision}.0.0" for revision in range(4, 55)) @dataclasses.dataclass(frozen=True) From 2c4f7aab0bf319f4a5ec1ccdbaf105629acf7acc Mon Sep 17 00:00:00 2001 From: Brad Edwards Date: Sun, 27 Sep 2026 06:57:58 +0200 Subject: [PATCH 03/11] ci: supply the SonarCloud token from AUTARCHY_SONAR_TOKEN --- .github/workflows/ci.yml | 2 +- implementations/python/tests/test_release_workflows.py | 2 +- tools/tooling_artifact_policy_actions.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5353215eb..833996217 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,7 +43,7 @@ jobs: # token below is never exposed to fork or Dependabot contexts. sonar-enabled: true secrets: - sonar_token: ${{ secrets.SONAR_TOKEN }} + sonar_token: ${{ secrets.AUTARCHY_SONAR_TOKEN }} # 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 diff --git a/implementations/python/tests/test_release_workflows.py b/implementations/python/tests/test_release_workflows.py index 4d3624ae3..402e4f20c 100644 --- a/implementations/python/tests/test_release_workflows.py +++ b/implementations/python/tests/test_release_workflows.py @@ -439,7 +439,7 @@ def test_ci_uses_the_same_canonical_verifier_for_github_sha() -> None: 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 }}" + assert canonical["secrets"]["sonar_token"] == "${{ secrets.AUTARCHY_SONAR_TOKEN }}" # dev/main branch protection requires the `verify` and `sonar` contexts. Both # are now decoupled joins over the reusable graph's outcomes (#935). diff --git a/tools/tooling_artifact_policy_actions.py b/tools/tooling_artifact_policy_actions.py index b74cfe186..eb8c9f125 100644 --- a/tools/tooling_artifact_policy_actions.py +++ b/tools/tooling_artifact_policy_actions.py @@ -49,8 +49,8 @@ def _sonar_boundary(path: str, name: str, job: Mapping[str, Any], workflows: Map 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 }}"} + and job.get("secrets") == {"sonar_token": "${{ secrets.AUTARCHY_SONAR_TOKEN }}"} + and _secret_expressions(job) == {"${{ secrets.AUTARCHY_SONAR_TOKEN }}"} ) From 952dffbb9b5352bebf0c9a20da7a41a112e931ca Mon Sep 17 00:00:00 2001 From: Brad Edwards Date: Sun, 27 Sep 2026 07:18:47 +0200 Subject: [PATCH 04/11] ci: keep supplying the SonarCloud token from SONAR_TOKEN --- .github/workflows/ci.yml | 2 +- implementations/python/tests/test_release_workflows.py | 2 +- tools/tooling_artifact_policy_actions.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 833996217..5353215eb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,7 +43,7 @@ jobs: # token below is never exposed to fork or Dependabot contexts. sonar-enabled: true secrets: - sonar_token: ${{ secrets.AUTARCHY_SONAR_TOKEN }} + sonar_token: ${{ secrets.SONAR_TOKEN }} # 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 diff --git a/implementations/python/tests/test_release_workflows.py b/implementations/python/tests/test_release_workflows.py index 402e4f20c..4d3624ae3 100644 --- a/implementations/python/tests/test_release_workflows.py +++ b/implementations/python/tests/test_release_workflows.py @@ -439,7 +439,7 @@ def test_ci_uses_the_same_canonical_verifier_for_github_sha() -> None: 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.AUTARCHY_SONAR_TOKEN }}" + assert canonical["secrets"]["sonar_token"] == "${{ secrets.SONAR_TOKEN }}" # dev/main branch protection requires the `verify` and `sonar` contexts. Both # are now decoupled joins over the reusable graph's outcomes (#935). diff --git a/tools/tooling_artifact_policy_actions.py b/tools/tooling_artifact_policy_actions.py index eb8c9f125..b74cfe186 100644 --- a/tools/tooling_artifact_policy_actions.py +++ b/tools/tooling_artifact_policy_actions.py @@ -49,8 +49,8 @@ def _sonar_boundary(path: str, name: str, job: Mapping[str, Any], workflows: Map and name == "canonical" and guarded and job.get("uses") == "./" + _CANONICAL - and job.get("secrets") == {"sonar_token": "${{ secrets.AUTARCHY_SONAR_TOKEN }}"} - and _secret_expressions(job) == {"${{ secrets.AUTARCHY_SONAR_TOKEN }}"} + and job.get("secrets") == {"sonar_token": "${{ secrets.SONAR_TOKEN }}"} + and _secret_expressions(job) == {"${{ secrets.SONAR_TOKEN }}"} ) From 6e5a879a3366337f18770e3f835810e690651bd1 Mon Sep 17 00:00:00 2001 From: Brad Edwards Date: Sun, 27 Sep 2026 08:23:29 +0200 Subject: [PATCH 05/11] ci: supply the SonarCloud token from PERSONAL_SONAR_TOKEN --- .github/workflows/ci.yml | 2 +- implementations/python/tests/test_release_workflows.py | 2 +- tools/tooling_artifact_policy_actions.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5353215eb..e08c98ed2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,7 +43,7 @@ jobs: # token below is never exposed to fork or Dependabot contexts. sonar-enabled: true secrets: - sonar_token: ${{ secrets.SONAR_TOKEN }} + sonar_token: ${{ secrets.PERSONAL_SONAR_TOKEN }} # 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 diff --git a/implementations/python/tests/test_release_workflows.py b/implementations/python/tests/test_release_workflows.py index 4d3624ae3..06503ca19 100644 --- a/implementations/python/tests/test_release_workflows.py +++ b/implementations/python/tests/test_release_workflows.py @@ -439,7 +439,7 @@ def test_ci_uses_the_same_canonical_verifier_for_github_sha() -> None: 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 }}" + assert canonical["secrets"]["sonar_token"] == "${{ secrets.PERSONAL_SONAR_TOKEN }}" # dev/main branch protection requires the `verify` and `sonar` contexts. Both # are now decoupled joins over the reusable graph's outcomes (#935). diff --git a/tools/tooling_artifact_policy_actions.py b/tools/tooling_artifact_policy_actions.py index b74cfe186..6ce6dd229 100644 --- a/tools/tooling_artifact_policy_actions.py +++ b/tools/tooling_artifact_policy_actions.py @@ -49,8 +49,8 @@ def _sonar_boundary(path: str, name: str, job: Mapping[str, Any], workflows: Map 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 }}"} + and job.get("secrets") == {"sonar_token": "${{ secrets.PERSONAL_SONAR_TOKEN }}"} + and _secret_expressions(job) == {"${{ secrets.PERSONAL_SONAR_TOKEN }}"} ) From 9242267a6ede6af3e30c50198c1bda92c4f9d75c Mon Sep 17 00:00:00 2001 From: Brad Edwards Date: Sun, 27 Sep 2026 08:52:23 +0200 Subject: [PATCH 06/11] ci: supply the SonarCloud token from SONAR_TOKEN --- .github/workflows/ci.yml | 2 +- implementations/python/tests/test_release_workflows.py | 2 +- tools/tooling_artifact_policy_actions.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e08c98ed2..5353215eb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,7 +43,7 @@ jobs: # token below is never exposed to fork or Dependabot contexts. sonar-enabled: true secrets: - sonar_token: ${{ secrets.PERSONAL_SONAR_TOKEN }} + sonar_token: ${{ secrets.SONAR_TOKEN }} # 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 diff --git a/implementations/python/tests/test_release_workflows.py b/implementations/python/tests/test_release_workflows.py index 06503ca19..4d3624ae3 100644 --- a/implementations/python/tests/test_release_workflows.py +++ b/implementations/python/tests/test_release_workflows.py @@ -439,7 +439,7 @@ def test_ci_uses_the_same_canonical_verifier_for_github_sha() -> None: 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.PERSONAL_SONAR_TOKEN }}" + assert canonical["secrets"]["sonar_token"] == "${{ secrets.SONAR_TOKEN }}" # dev/main branch protection requires the `verify` and `sonar` contexts. Both # are now decoupled joins over the reusable graph's outcomes (#935). diff --git a/tools/tooling_artifact_policy_actions.py b/tools/tooling_artifact_policy_actions.py index 6ce6dd229..b74cfe186 100644 --- a/tools/tooling_artifact_policy_actions.py +++ b/tools/tooling_artifact_policy_actions.py @@ -49,8 +49,8 @@ def _sonar_boundary(path: str, name: str, job: Mapping[str, Any], workflows: Map and name == "canonical" and guarded and job.get("uses") == "./" + _CANONICAL - and job.get("secrets") == {"sonar_token": "${{ secrets.PERSONAL_SONAR_TOKEN }}"} - and _secret_expressions(job) == {"${{ secrets.PERSONAL_SONAR_TOKEN }}"} + and job.get("secrets") == {"sonar_token": "${{ secrets.SONAR_TOKEN }}"} + and _secret_expressions(job) == {"${{ secrets.SONAR_TOKEN }}"} ) From 6b1d7f65c1c11371faa914983c412636bd81eb62 Mon Sep 17 00:00:00 2001 From: Brad Edwards Date: Sun, 27 Sep 2026 23:14:46 +0200 Subject: [PATCH 07/11] fix: seal the control-plane app and harden transport admission refusals --- ...trol-plane-participant-access-preflight.md | 6 +- docs/public/guides/control-plane.md | 19 +- docs/requirements/API-404/requirement.md | 13 +- .../analysis-v55.json | 156 ++++ .../bundles/retest-v55.json | 122 +++ .../execution-snapshot-v55.json | 652 ++++++++++++++++ .../formal-semantic-validation/index.md | 8 +- .../specification-coverage/analysis-v55.json | 105 +++ ...specification-coverage-issue-1359-v55.json | 10 + .../execution-snapshot-v55.json | 700 ++++++++++++++++++ docs/research/specification-coverage/index.md | 9 +- .../control_plane_api/__init__.py | 29 +- .../raes_runtime/control_plane_api/_auth.py | 99 ++- .../control_plane_api/_operation_routes.py | 44 +- .../raes_runtime/control_plane_api_guards.py | 48 +- .../raes_runtime/control_plane_security.py | 36 +- .../tests/test_formal_semantic_validation.py | 5 +- .../test_issue_1179_startup_reconciliation.py | 26 + ...t_issue_1359_runtime_api_trust_boundary.py | 155 +++- .../test_issue_989_versioned_evidence.py | 6 +- .../tests/test_runtime_control_plane_api.py | 8 +- .../tests/test_specification_coverage.py | 2 +- tools/check_specification_coverage.py | 6 +- tools/formal_semantic_validation/_baseline.py | 1 + tools/formal_semantic_validation/_loading.py | 4 +- .../_release_revisions.py | 4 +- tools/formal_semantic_validation/_releases.py | 6 +- tools/formal_semantic_validation/_retest.py | 2 +- 28 files changed, 2223 insertions(+), 58 deletions(-) create mode 100644 docs/research/formal-semantic-validation/analysis-v55.json create mode 100644 docs/research/formal-semantic-validation/bundles/retest-v55.json create mode 100644 docs/research/formal-semantic-validation/execution-snapshot-v55.json create mode 100644 docs/research/specification-coverage/analysis-v55.json create mode 100644 docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1359-v55.json create mode 100644 docs/research/specification-coverage/execution-snapshot-v55.json diff --git a/docs/decisions/issue-1356-control-plane-participant-access-preflight.md b/docs/decisions/issue-1356-control-plane-participant-access-preflight.md index eb7a6d7cb..338a1252a 100644 --- a/docs/decisions/issue-1356-control-plane-participant-access-preflight.md +++ b/docs/decisions/issue-1356-control-plane-participant-access-preflight.md @@ -80,7 +80,11 @@ are never participant-view fallbacks. This table inventories the optional reference P2 HTTP app. `read` is the existing `_ReadIdentity` role check, `mutate` is `_MutatingIdentity`, and -`resolve` is `_ResolutionIdentity`. All authenticated P2 identities require +`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. diff --git a/docs/public/guides/control-plane.md b/docs/public/guides/control-plane.md index 399ec8fbc..04f7f4b55 100644 --- a/docs/public/guides/control-plane.md +++ b/docs/public/guides/control-plane.md @@ -27,7 +27,9 @@ 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. +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 | | --- | --- | --- | @@ -87,8 +89,17 @@ 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. @@ -124,7 +135,11 @@ client-supplied identity headers and sets verified ones. ## Add a route to the adapter -Declare one transport authority with the dependencies in +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/requirements/API-404/requirement.md b/docs/requirements/API-404/requirement.md index 8ce9e507b..c6a5d0621 100644 --- a/docs/requirements/API-404/requirement.md +++ b/docs/requirements/API-404/requirement.md @@ -68,10 +68,15 @@ selected P1 core. The corresponding runtime guarantee identifiers are 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, administrative read, administrative mutation -or operator resolution. P2 composition shall fail when any other route is -registered. Every P2 response, including errors and idempotent readback, shall -carry `Cache-Control: no-store`. +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 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..38db8bf9e --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v55.json @@ -0,0 +1,156 @@ +{ + "analysis_id": "issue-1359-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-1359-execution-v55", + "generated_at": "2026-09-27T21:09:43.092528+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 #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/bundles/retest-v55.json b/docs/research/formal-semantic-validation/bundles/retest-v55.json new file mode 100644 index 000000000..96178c9e8 --- /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": "129e23530506406f45c5af1529bd5c5a96b4855f929074086054b1d5c4c37e75", + "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": "149cdd748c92917c7be76b78ba334266e23e0ac6e961828d8c484f648cd203ed" +} 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..ed040e2b6 --- /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-27T21:09:43.092528+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-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-1359-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-1359-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-1359-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-1359-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-1359-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-1359-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-1359-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-1359-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-1359-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-1359-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-1359-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-1359-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-1359-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-1359-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-1359-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-1359-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-1359-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-1359-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-1359-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-1359-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-1359-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-1359-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-1359-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-1359-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-1359-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-1359-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-1359-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": "8e8d3b5f31b06f30792b6813da3d32d4b6297315", + "source_state": { + "base_revision": "8e8d3b5f31b06f30792b6813da3d32d4b6297315", + "checkout_state": "modified", + "implementation_digest": "f98c5f1fa78a8afeb196185507702dbd92e38fcac8a05169b0833ed6639be9f3", + "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/index.md b/docs/research/formal-semantic-validation/index.md index 74cfd2465..fff14dcfc 100644 --- a/docs/research/formal-semantic-validation/index.md +++ b/docs/research/formal-semantic-validation/index.md @@ -336,7 +336,7 @@ 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 55.0.0, rejects unsupported future +Current validation requires explicit release 56.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 @@ -506,3 +506,9 @@ Release 55.0.0 is recorded in [`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 is recorded in +[`execution-snapshot-v55.json`](execution-snapshot-v55.json) and +[`analysis-v55.json`](analysis-v55.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. diff --git a/docs/research/specification-coverage/analysis-v55.json b/docs/research/specification-coverage/analysis-v55.json new file mode 100644 index 000000000..0aa6aa863 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v55.json @@ -0,0 +1,105 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-1359-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-27T21:09:43.092528+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 #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-v55", + "snapshot_sha256": "b31819c1ec7ce787b3f92c5940087c62f60feca134a4c0f57aa8b4f9bd541b84" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1359-v55.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1359-v55.json new file mode 100644 index 000000000..80e018c0e --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1359-v55.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v55.json", + "analysis_sha256": "29453cd571333a8fbe2289f452e132c0a3a833aadd9e974c61db1ee46a808e43", + "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": "fe36723ac648c009f3481dcc53fdda32080072bab311794c551c96034d04237c" +} 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..24eacda93 --- /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-27T21:09:43.092528+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": "8e8d3b5f31b06f30792b6813da3d32d4b6297315", + "snapshot_id": "raes-standardized-specification-coverage-issue-1359-v55", + "snapshot_revision": "55.0.0", + "source_state": { + "base_revision": "8e8d3b5f31b06f30792b6813da3d32d4b6297315", + "checkout_state": "modified", + "implementation_digest": "f98c5f1fa78a8afeb196185507702dbd92e38fcac8a05169b0833ed6639be9f3", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/index.md b/docs/research/specification-coverage/index.md index af9bd738b..ba3d8c882 100644 --- a/docs/research/specification-coverage/index.md +++ b/docs/research/specification-coverage/index.md @@ -292,7 +292,7 @@ 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 54.0.0 and rejects duplicate or unsupported +Current validation requires release 55.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; @@ -443,3 +443,10 @@ 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 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-v55.json`](execution-snapshot-v55.json) and +[`analysis-v55.json`](analysis-v55.json). 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 f2b2c00e7..6b8fe1a86 100644 --- a/implementations/python/packages/raes_runtime/control_plane_api/__init__.py +++ b/implementations/python/packages/raes_runtime/control_plane_api/__init__.py @@ -30,8 +30,10 @@ 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, @@ -41,7 +43,12 @@ ) from ..control_plane_security import ControlPlaneSecurityConfig from ..control_plane_store_lease import require_single_worker_configuration -from ._auth import _ControlPlaneApiAuth, _require_route_transport_authority +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 @@ -79,6 +86,20 @@ 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, *, @@ -103,14 +124,15 @@ def create_control_plane_app( executor = _ControlPlaneCallExecutor(max_pending_mutations=security.max_pending_mutations) @asynccontextmanager - async def lifespan(_app: FastAPI) -> AsyncIterator[None]: + 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 = FastAPI( + app = _ControlPlaneFastAPI( title="RAES Runtime Control Plane", version=_control_plane_api_version(), description="Reference HTTP/JSON adapter over the repo-owned runtime control plane.", @@ -128,4 +150,5 @@ async def lifespan(_app: FastAPI) -> AsyncIterator[None]: _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 8ec020ec8..8489d749b 100644 --- a/implementations/python/packages/raes_runtime/control_plane_api/_auth.py +++ b/implementations/python/packages/raes_runtime/control_plane_api/_auth.py @@ -21,6 +21,7 @@ from ..control_plane import RuntimeControlPlane from ..control_plane_security import ( + ROUTE_AUTHORITY_METHODS, ROUTE_AUTHORITY_ROLES, ControlPlaneIdentity, ControlPlaneRole, @@ -29,6 +30,17 @@ ) _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: @@ -47,11 +59,15 @@ def admit(self, request: Request, authority: ControlPlaneRouteAuthority) -> Cont 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", "") @@ -63,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: @@ -93,7 +109,7 @@ 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( @@ -111,7 +127,7 @@ def _authorize( allowed=False, 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._record_audit_safely( @@ -174,8 +190,10 @@ def _declared_route_authority(route: APIRoute) -> ControlPlaneRouteAuthority: if len(declared) != 1: raise ValueError(f"route {sorted(route.methods)} {route.path} must declare exactly one transport authority") authority = declared[0] - if authority is ControlPlaneRouteAuthority.PUBLIC_PROBE and route.methods != {"GET"}: - raise ValueError(f"public-probe transport authority is GET-only: {route.path}") + if not route.methods or not route.methods <= ROUTE_AUTHORITY_METHODS[authority]: + raise ValueError( + f"{authority.value} transport authority cannot serve {sorted(route.methods or ())} {route.path}" + ) return authority @@ -186,8 +204,10 @@ def _require_route_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, so a new operation, inject, event or export route - cannot silently bypass P2 admission. + 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`). """ framework_paths = {app.openapi_url, app.docs_url, app.redoc_url, app.swagger_ui_oauth2_redirect_url} - {None} @@ -202,3 +222,56 @@ def _require_route_transport_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 _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") + + +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/_operation_routes.py b/implementations/python/packages/raes_runtime/control_plane_api/_operation_routes.py index 814685bb6..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 @@ -16,9 +16,15 @@ ProvisioningPlanModel, RuntimeSnapshotEnvelopeModel, ) +from starlette.concurrency import run_in_threadpool from ..control_plane import RuntimeControlPlane -from ..control_plane_api_guards import NO_STORE_CACHE_CONTROL, NoStoreResponseMiddleware, 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, @@ -27,8 +33,13 @@ _snapshot_model, ) from ..control_plane_recovery import IndeterminateResolutionDisposition -from ..control_plane_security import ControlPlaneSecurityConfig -from ._auth import _AdministrativeMutationIdentity, _AdministrativeReadIdentity, _OperatorResolutionIdentity +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, @@ -83,6 +94,8 @@ def _install_request_guards( 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, @@ -107,12 +120,31 @@ async def _redacted_errors(request: Request, exc: Exception) -> JSONResponse: @app.exception_handler(RequestValidationError) async def _redacted_request_validation_errors(request: Request, exc: RequestValidationError) -> JSONResponse: del exc + # 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, @@ -150,10 +182,10 @@ async def resolve_indeterminate_operation( idempotency_key=request.headers.get("idempotency-key", ""), identity=identity, ) - except KeyError as exc: + 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 PermissionError as exc: - raise HTTPException(status_code=403, detail="indeterminate resolution forbidden") from exc except (TypeError, ValueError, RuntimeError) as exc: raise HTTPException(status_code=409, detail="indeterminate resolution conflict") 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 6ef3e973f..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 @@ -88,6 +88,52 @@ async def send_uncacheable(message: Message) -> None: 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. diff --git a/implementations/python/packages/raes_runtime/control_plane_security.py b/implementations/python/packages/raes_runtime/control_plane_security.py index ff7d787e3..f73213a6c 100644 --- a/implementations/python/packages/raes_runtime/control_plane_security.py +++ b/implementations/python/packages/raes_runtime/control_plane_security.py @@ -55,6 +55,21 @@ class ControlPlaneRouteAuthority(str, Enum): 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. @@ -157,8 +172,10 @@ def _require_principal_shape(principal: ControlPlaneIdentity) -> None: def _frozen_principals(principals: Mapping[str, ControlPlaneIdentity]) -> Mapping[str, ControlPlaneIdentity]: copied = dict(principals) for key, principal in copied.items(): - if not isinstance(key, str) or not key.strip(): - raise ValueError("configured credentials and principal names must be non-empty") + # 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) @@ -189,10 +206,17 @@ class ControlPlaneSecurityConfig: def __post_init__(self) -> None: _require_identity_headers(self.verified_header, self.identity_header) - 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") + # 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``. diff --git a/implementations/python/tests/test_formal_semantic_validation.py b/implementations/python/tests/test_formal_semantic_validation.py index 812bf74be..9866d6dad 100644 --- a/implementations/python/tests/test_formal_semantic_validation.py +++ b/implementations/python/tests/test_formal_semantic_validation.py @@ -134,6 +134,7 @@ def test_atomic_release_index_validates_every_historical_bundle() -> None: "53.0.0", "54.0.0", "55.0.0", + "56.0.0", ] assert all(validate_release_bundle(REPO_ROOT, release) == [] for release in releases) @@ -142,10 +143,10 @@ 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"] == "55.0.0" + assert release.manifest["revision"] == "56.0.0" assert protocol["revision"] == "2.0.0" assert corpus["revision"] == "4.0.0" - assert snapshot["baseline"]["release_revision"] == "54.0.0" + assert snapshot["baseline"]["release_revision"] == "55.0.0" assert snapshot["deviations"] == [] assert validate_retest_bundle(REPO_ROOT, release, protocol, corpus, snapshot, analysis) == [] diff --git a/implementations/python/tests/test_issue_1179_startup_reconciliation.py b/implementations/python/tests/test_issue_1179_startup_reconciliation.py index 5c43d9b9c..ed6f9191a 100644 --- a/implementations/python/tests/test_issue_1179_startup_reconciliation.py +++ b/implementations/python/tests/test_issue_1179_startup_reconciliation.py @@ -580,3 +580,29 @@ def test_http_resolution_is_operator_only_and_returns_stable_errors() -> None: ) 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_1359_runtime_api_trust_boundary.py b/implementations/python/tests/test_issue_1359_runtime_api_trust_boundary.py index 122381d31..7f2a3d349 100644 --- a/implementations/python/tests/test_issue_1359_runtime_api_trust_boundary.py +++ b/implementations/python/tests/test_issue_1359_runtime_api_trust_boundary.py @@ -28,6 +28,7 @@ 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, @@ -273,6 +274,19 @@ 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=[])) @@ -287,6 +301,8 @@ def _starlette_route(app: FastAPI) -> None: 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"), @@ -302,6 +318,79 @@ def test_app_construction_fails_closed_for_undeclared_route_authority( create_control_plane_app(RuntimeControlPlane(create_stub_target())) +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) + + with pytest.raises(RuntimeError), TestClient(app): + 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 ----------------------- @@ -318,6 +407,47 @@ def test_authenticated_routes_reject_unauthenticated_callers( 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 @@ -328,7 +458,7 @@ def test_authenticated_routes_reject_identities_bound_to_another_target( response = _send(client, method, path, body, _bearer("other-target-token")) assert response.status_code == 403 - assert response.json() == {"detail": "identity is not authorized for this target"} + assert response.json() == {"detail": "forbidden"} @pytest.mark.parametrize(("method", "template", "path", "body"), _route_requests(_READ, _MUTATE, _RESOLVE)) @@ -692,6 +822,29 @@ def test_security_config_rejects_malformed_principal_shapes(principal: str, mapp 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: + with pytest.raises(ValueError, match="unpadded") as caught: + ControlPlaneSecurityConfig(**{mapping: {key: _identity("padded", ControlPlaneRole.AUDITOR)}}) + + assert "secret" not in str(caught.value) + + def test_principal_at_the_operation_context_bounds_is_admitted() -> None: principal = _identity( "a" * 256, diff --git a/implementations/python/tests/test_issue_989_versioned_evidence.py b/implementations/python/tests/test_issue_989_versioned_evidence.py index 707f08176..42f4f2753 100644 --- a/implementations/python/tests/test_issue_989_versioned_evidence.py +++ b/implementations/python/tests/test_issue_989_versioned_evidence.py @@ -230,7 +230,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"] == "55.0.0" + assert release.manifest["revision"] == "56.0.0" original = _retest.replay_case def changed_result(root, case): @@ -283,7 +283,7 @@ def test_specification_current_capture_does_not_accept_old_artifact_digest(artif from tools.check_specification_coverage import load_bundle, validate_bundle manifest, protocol, snapshot, analysis = copy_bundle(load_bundle, ROOT) - assert manifest["revision"] == "54.0.0" + assert manifest["revision"] == "55.0.0" snapshot = deepcopy(snapshot) artifact = next(a for a in snapshot["artifacts"] if a["artifact_id"] == artifact_id) artifact["sha256"] = old_digest @@ -507,6 +507,7 @@ def test_no_capture_can_be_silently_dropped(monkeypatch, family, removed): "53.0.0", "54.0.0", "55.0.0", + "56.0.0", ] if family == "formal" else [ @@ -565,6 +566,7 @@ def test_no_capture_can_be_silently_dropped(monkeypatch, family, removed): "52.0.0", "53.0.0", "54.0.0", + "55.0.0", ] ) revisions.pop(-1 if removed == "current" else 0) diff --git a/implementations/python/tests/test_runtime_control_plane_api.py b/implementations/python/tests/test_runtime_control_plane_api.py index 81c4a32cc..4c983b714 100644 --- a/implementations/python/tests/test_runtime_control_plane_api.py +++ b/implementations/python/tests/test_runtime_control_plane_api.py @@ -1695,7 +1695,7 @@ def test_control_plane_api_rejects_invalid_bearer_token_instead_of_trusting_head audits = control_plane.audit_log() assert response.status_code == 401 - assert response.json() == {"detail": "invalid bearer token"} + assert response.json() == {"detail": "unauthorized"} assert audits[-1].reason == "invalid bearer token" assert audits[-1].allowed is False @@ -1725,7 +1725,7 @@ def test_control_plane_auth_rejects_non_ascii_bearer_token_as_unauthorized(): 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(): @@ -1755,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"]) @@ -1784,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(): diff --git a/implementations/python/tests/test_specification_coverage.py b/implementations/python/tests/test_specification_coverage.py index cb3d4c9fc..85fb6425e 100644 --- a/implementations/python/tests/test_specification_coverage.py +++ b/implementations/python/tests/test_specification_coverage.py @@ -53,7 +53,7 @@ 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", "19.0.0"} manifest, *_rest = copy_bundle(load_bundle, REPO_ROOT) - assert manifest["revision"] == "54.0.0" + assert manifest["revision"] == "55.0.0" def test_historical_failures_name_the_revision_specific_documents() -> None: diff --git a/tools/check_specification_coverage.py b/tools/check_specification_coverage.py index 39cdc899c..f9370fe6a 100644 --- a/tools/check_specification_coverage.py +++ b/tools/check_specification_coverage.py @@ -157,12 +157,12 @@ def _load_bundle_index(repo_root: Path) -> list[tuple[str, dict[str, object]]]: "51.0.0", "52.0.0", "53.0.0", - } | {"54.0.0"} + } | {"54.0.0", "55.0.0"} if ( - dict(records)[current_path].get("revision") != "54.0.0" + dict(records)[current_path].get("revision") != "55.0.0" or {record.get("revision") for _, record in records} != supported_revisions ): - raise ValueError("coverage evidence requires the explicit current 54.0.0 release and supported history") + raise ValueError("coverage evidence requires the explicit current 55.0.0 release and supported history") return records diff --git a/tools/formal_semantic_validation/_baseline.py b/tools/formal_semantic_validation/_baseline.py index a7b4b7840..540c7751b 100644 --- a/tools/formal_semantic_validation/_baseline.py +++ b/tools/formal_semantic_validation/_baseline.py @@ -190,6 +190,7 @@ def _selected_baseline_manifest( "52.0.0", "53.0.0", "54.0.0", + "55.0.0", }: expected_corpus_path = "docs/research/formal-semantic-validation/corpus/manifest-v4.json" elif baseline_revision in _V3_CORPUS_REVISIONS: diff --git a/tools/formal_semantic_validation/_loading.py b/tools/formal_semantic_validation/_loading.py index c7b8ba558..c56b5f44b 100644 --- a/tools/formal_semantic_validation/_loading.py +++ b/tools/formal_semantic_validation/_loading.py @@ -75,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") != "55.0.0" or release.protocol.get("revision") != "2.0.0": - raise ValueError("the current formal evidence release must be the explicit 55.0.0 retest") + if release.manifest.get("revision") != "56.0.0" or release.protocol.get("revision") != "2.0.0": + raise ValueError("the current formal evidence release must be the explicit 56.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 index b818c373b..b1a838b0d 100644 --- a/tools/formal_semantic_validation/_release_revisions.py +++ b/tools/formal_semantic_validation/_release_revisions.py @@ -53,7 +53,7 @@ "51.0.0", "52.0.0", } -) | {"53.0.0", "54.0.0"} +) | {"53.0.0", "54.0.0", "55.0.0"} -_SUPPORTED_RETEST_REVISIONS = _HISTORICAL_RETEST_REVISIONS | {"55.0.0"} +_SUPPORTED_RETEST_REVISIONS = _HISTORICAL_RETEST_REVISIONS | {"56.0.0"} _SOURCE_BOUND_RETEST_REVISIONS = _SUPPORTED_RETEST_REVISIONS - {"3.0.0"} diff --git a/tools/formal_semantic_validation/_releases.py b/tools/formal_semantic_validation/_releases.py index aa4a05987..5e9f92e86 100644 --- a/tools/formal_semantic_validation/_releases.py +++ b/tools/formal_semantic_validation/_releases.py @@ -159,7 +159,7 @@ def validate_release_bundle(repo_root: Path, release: EvidenceRelease) -> list[P release.corpus, release.snapshot, release.analysis, - replay_current=manifest.get("revision") == "55.0.0", + replay_current=manifest.get("revision") == "56.0.0", ) ) else: @@ -272,6 +272,7 @@ def _expected_corpus_revision(release_revision: object) -> str: "53.0.0", "54.0.0", "55.0.0", + "56.0.0", }: expected_corpus_revision = "4.0.0" return expected_corpus_revision @@ -299,7 +300,7 @@ def validate_retest_bundle( return [ _failure( "formal-validation-current-replay-required", - "only releases 3.0.0 through 54.0.0 can use integrated historical validation", + "only releases 3.0.0 through 55.0.0 can use integrated historical validation", snapshot_path, ) ] @@ -427,6 +428,7 @@ def _current_retest_source_failures( "53.0.0": "52.0.0", "54.0.0": "53.0.0", "55.0.0": "54.0.0", + "56.0.0": "55.0.0", }[release_revision] if not isinstance(baseline, Mapping) or baseline.get("release_revision") != expected_baseline: failures.append( diff --git a/tools/formal_semantic_validation/_retest.py b/tools/formal_semantic_validation/_retest.py index cbe06846f..6e6c95161 100644 --- a/tools/formal_semantic_validation/_retest.py +++ b/tools/formal_semantic_validation/_retest.py @@ -37,7 +37,7 @@ ) from tools.policy.common import PolicyFailure -_SOURCE_STATE_REVISIONS = frozenset(f"{revision}.0.0" for revision in range(4, 56)) +_SOURCE_STATE_REVISIONS = frozenset(f"{revision}.0.0" for revision in range(4, 57)) @dataclasses.dataclass(frozen=True) From 0b8778eb5204d963429df94fdafe83d8382172a4 Mon Sep 17 00:00:00 2001 From: Brad Edwards Date: Sun, 27 Sep 2026 23:15:36 +0200 Subject: [PATCH 08/11] docs: record the sealed composition and refusal guarantees in API-404 traceability --- docs/requirements/API-404/requirement.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/requirements/API-404/requirement.md b/docs/requirements/API-404/requirement.md index c6a5d0621..4ae390a9e 100644 --- a/docs/requirements/API-404/requirement.md +++ b/docs/requirements/API-404/requirement.md @@ -144,14 +144,15 @@ identifies those implementation gaps and the retained canonical requirements. - 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, unambiguous subject bindings and fail-closed configured-principal shape and bound validation) -- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_api/_auth.py` (One declared transport authority per served route and fail-closed route inventory) -- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_api/__init__.py` (Route-authority inventory enforced at app construction 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) -- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_api/_operation_routes.py` (No-store installation and uncacheable redacted 500 envelope) +- 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, per-route admission, governed and legacy views, replay scope, no-store and principal-shape checks) +- 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) From 092b83f763ba2e3e230af940a9e916173abf7b5f Mon Sep 17 00:00:00 2001 From: Brad Edwards Date: Sun, 27 Sep 2026 23:40:34 +0200 Subject: [PATCH 09/11] Fix SonarCloud findings (cycle 1) --- .../formal-semantic-validation/analysis-v55.json | 2 +- .../bundles/retest-v55.json | 4 ++-- .../execution-snapshot-v55.json | 4 ++-- .../specification-coverage/analysis-v55.json | 4 ++-- ...dized-specification-coverage-issue-1359-v55.json | 4 ++-- .../execution-snapshot-v55.json | 4 ++-- .../raes_runtime/control_plane_api/_auth.py | 2 +- .../test_issue_1359_runtime_api_trust_boundary.py | 13 +++++++++---- 8 files changed, 21 insertions(+), 16 deletions(-) diff --git a/docs/research/formal-semantic-validation/analysis-v55.json b/docs/research/formal-semantic-validation/analysis-v55.json index 38db8bf9e..a6ade9e88 100644 --- a/docs/research/formal-semantic-validation/analysis-v55.json +++ b/docs/research/formal-semantic-validation/analysis-v55.json @@ -124,7 +124,7 @@ "corpus_revision": "4.0.0", "evidence_status": "partial", "execution_id": "issue-1359-execution-v55", - "generated_at": "2026-09-27T21:09:43.092528+00:00", + "generated_at": "2026-09-27T21:38:57.468381+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.", diff --git a/docs/research/formal-semantic-validation/bundles/retest-v55.json b/docs/research/formal-semantic-validation/bundles/retest-v55.json index 96178c9e8..e2c101780 100644 --- a/docs/research/formal-semantic-validation/bundles/retest-v55.json +++ b/docs/research/formal-semantic-validation/bundles/retest-v55.json @@ -1,6 +1,6 @@ { "analysis_path": "docs/research/formal-semantic-validation/analysis-v55.json", - "analysis_sha256": "129e23530506406f45c5af1529bd5c5a96b4855f929074086054b1d5c4c37e75", + "analysis_sha256": "d210d11a4586c371712e0efc288efee0129dd9f34ac1da642806663fd6ce3f27", "artifacts": [ { "artifact_id": "finite-domain-satisfiable-v2-input", @@ -118,5 +118,5 @@ "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", "revision": "56.0.0", "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v55.json", - "snapshot_sha256": "149cdd748c92917c7be76b78ba334266e23e0ac6e961828d8c484f648cd203ed" + "snapshot_sha256": "c266e0485a133a1c550ea2d2b7c2e03b120b49878e5ca32d140f2b975acbbde4" } diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v55.json b/docs/research/formal-semantic-validation/execution-snapshot-v55.json index ed040e2b6..3e7329372 100644 --- a/docs/research/formal-semantic-validation/execution-snapshot-v55.json +++ b/docs/research/formal-semantic-validation/execution-snapshot-v55.json @@ -5,7 +5,7 @@ "release_revision": "55.0.0", "release_sha256": "89591fe7e90a57a5d6047eb442171a6c1b1fdac7e5cf13ef458a374dd0a54398" }, - "captured_at": "2026-09-27T21:09:43.092528+00:00", + "captured_at": "2026-09-27T21:38:57.468381+00:00", "commands": [ { "argv": [ @@ -640,7 +640,7 @@ "source_state": { "base_revision": "8e8d3b5f31b06f30792b6813da3d32d4b6297315", "checkout_state": "modified", - "implementation_digest": "f98c5f1fa78a8afeb196185507702dbd92e38fcac8a05169b0833ed6639be9f3", + "implementation_digest": "f2427ee38deeddc72bad8197e060b6f4285faa4b7a512df0831c8e4145ccdee3", "profile": "python-reference-source/v2" }, "versions": { diff --git a/docs/research/specification-coverage/analysis-v55.json b/docs/research/specification-coverage/analysis-v55.json index 0aa6aa863..c0031a4b7 100644 --- a/docs/research/specification-coverage/analysis-v55.json +++ b/docs/research/specification-coverage/analysis-v55.json @@ -39,7 +39,7 @@ }, "evidence_status": "partial", "execution_status": "complete", - "generated_at": "2026-09-27T21:09:43.092528+00:00", + "generated_at": "2026-09-27T21:38:57.468381+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.", @@ -101,5 +101,5 @@ } ], "snapshot_id": "raes-standardized-specification-coverage-issue-1359-v55", - "snapshot_sha256": "b31819c1ec7ce787b3f92c5940087c62f60feca134a4c0f57aa8b4f9bd541b84" + "snapshot_sha256": "1a194c5a8d11cb7c1f0f5227da1bbc2fcb8123ca85f8f68f1eb439a957097780" } diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1359-v55.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1359-v55.json index 80e018c0e..d419fafa3 100644 --- a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1359-v55.json +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1359-v55.json @@ -1,10 +1,10 @@ { "analysis_path": "docs/research/specification-coverage/analysis-v55.json", - "analysis_sha256": "29453cd571333a8fbe2289f452e132c0a3a833aadd9e974c61db1ee46a808e43", + "analysis_sha256": "1d79fc521212cc1195b6957269727160119596a83c7630c44c0666a8a65a6dd1", "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": "fe36723ac648c009f3481dcc53fdda32080072bab311794c551c96034d04237c" + "snapshot_sha256": "842a217585eaa9f116e6bc3c39bb3ccd73e1d7bb1f7c2bb6085ea6caf38730cc" } diff --git a/docs/research/specification-coverage/execution-snapshot-v55.json b/docs/research/specification-coverage/execution-snapshot-v55.json index 24eacda93..65f564f4b 100644 --- a/docs/research/specification-coverage/execution-snapshot-v55.json +++ b/docs/research/specification-coverage/execution-snapshot-v55.json @@ -47,7 +47,7 @@ "release_revision": "1.1.0", "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" }, - "captured_at": "2026-09-27T21:09:43.092528+00:00", + "captured_at": "2026-09-27T21:38:57.468381+00:00", "concept_results": [ { "backend_support": "not-evaluated", @@ -694,7 +694,7 @@ "source_state": { "base_revision": "8e8d3b5f31b06f30792b6813da3d32d4b6297315", "checkout_state": "modified", - "implementation_digest": "f98c5f1fa78a8afeb196185507702dbd92e38fcac8a05169b0833ed6639be9f3", + "implementation_digest": "f2427ee38deeddc72bad8197e060b6f4285faa4b7a512df0831c8e4145ccdee3", "profile": "python-reference-source/v2" } } 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 8489d749b..04589bd6c 100644 --- a/implementations/python/packages/raes_runtime/control_plane_api/_auth.py +++ b/implementations/python/packages/raes_runtime/control_plane_api/_auth.py @@ -190,7 +190,7 @@ def _declared_route_authority(route: APIRoute) -> ControlPlaneRouteAuthority: 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 <= ROUTE_AUTHORITY_METHODS[authority]: + 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}" ) 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 index 7f2a3d349..9bb372785 100644 --- a/implementations/python/tests/test_issue_1359_runtime_api_trust_boundary.py +++ b/implementations/python/tests/test_issue_1359_runtime_api_trust_boundary.py @@ -313,9 +313,10 @@ def test_app_construction_fails_closed_for_undeclared_route_authority( 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(RuntimeControlPlane(create_stub_target())) + create_control_plane_app(control_plane) def _late_route(app: FastAPI) -> None: @@ -371,8 +372,9 @@ def test_app_composition_changed_after_construction_is_never_served(tamper: Call 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), TestClient(app): + with pytest.raises(RuntimeError), client: pass @@ -815,9 +817,10 @@ def _audience_bindings(count: int, *, ref_length: int = 16) -> tuple[Participant @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(**{mapping: {secret: _MALFORMED_PRINCIPALS[principal]()}}) + ControlPlaneSecurityConfig(**principals) assert secret not in str(caught.value) @@ -839,8 +842,10 @@ def test_security_config_rejects_non_int_limits(field: str, value: object) -> No @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(**{mapping: {key: _identity("padded", ControlPlaneRole.AUDITOR)}}) + ControlPlaneSecurityConfig(**principals) assert "secret" not in str(caught.value) From f1b275ef5c3b4828b1ebb39164facea8b7417432 Mon Sep 17 00:00:00 2001 From: Brad Edwards Date: Wed, 30 Sep 2026 06:26:01 +0200 Subject: [PATCH 10/11] test: republish research evidence captures above the #1399 releases --- .../analysis-v60.json | 157 ++++ .../bundles/retest-v60.json | 122 +++ .../execution-snapshot-v60.json | 652 ++++++++++++++++ .../formal-semantic-validation/index.md | 8 +- .../specification-coverage/analysis-v60.json | 107 +++ ...specification-coverage-issue-1359-v60.json | 10 + .../execution-snapshot-v60.json | 702 ++++++++++++++++++ docs/research/specification-coverage/index.md | 9 +- .../tests/test_formal_semantic_validation.py | 5 +- .../test_issue_989_versioned_evidence.py | 6 +- .../tests/test_specification_coverage.py | 2 +- tools/check_specification_coverage.py | 6 +- tools/formal_semantic_validation/_baseline.py | 1 + tools/formal_semantic_validation/_loading.py | 4 +- .../_release_revisions.py | 5 +- tools/formal_semantic_validation/_releases.py | 5 +- tools/formal_semantic_validation/_retest.py | 2 +- 17 files changed, 1786 insertions(+), 17 deletions(-) create mode 100644 docs/research/formal-semantic-validation/analysis-v60.json create mode 100644 docs/research/formal-semantic-validation/bundles/retest-v60.json create mode 100644 docs/research/formal-semantic-validation/execution-snapshot-v60.json create mode 100644 docs/research/specification-coverage/analysis-v60.json create mode 100644 docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1359-v60.json create mode 100644 docs/research/specification-coverage/execution-snapshot-v60.json diff --git a/docs/research/formal-semantic-validation/analysis-v60.json b/docs/research/formal-semantic-validation/analysis-v60.json new file mode 100644 index 000000000..96c924d33 --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v60.json @@ -0,0 +1,157 @@ +{ + "analysis_id": "issue-1359-analysis-v60", + "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-v60.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-v60", + "generated_at": "2026-09-30T04:21:21.856964+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.", + "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.", + "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/bundles/retest-v60.json b/docs/research/formal-semantic-validation/bundles/retest-v60.json new file mode 100644 index 000000000..5284996ea --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v60.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v60.json", + "analysis_sha256": "49d1cc85df03b51778c1f3bdfad74630d661d3958ec043cb5db14c6120dc6cfb", + "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-v60.json", + "snapshot_sha256": "c5e721a5b44678af012ced61248651c9ef5199265faf00cfd015da7b65c838aa" +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v60.json b/docs/research/formal-semantic-validation/execution-snapshot-v60.json new file mode 100644 index 000000000..69d72a009 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v60.json @@ -0,0 +1,652 @@ +{ + "baseline": { + "execution_id": "issue-1365-execution-v59", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v59.json", + "release_revision": "60.0.0", + "release_sha256": "08e87e3e1531dd445d88227b2fb541f9c8c2846f621d29275e5f7fedca120a3b" + }, + "captured_at": "2026-09-30T04:21:21.856964+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-v60", + "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-v60", + "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-v60", + "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-v60", + "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-v60", + "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-v60", + "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-v60", + "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-v60", + "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-v60", + "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-v60", + "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-v60", + "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-v60", + "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-v60", + "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-v60", + "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-v60", + "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-v60", + "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-v60", + "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-v60", + "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-v60", + "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-v60", + "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-v60", + "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-v60", + "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-v60", + "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-v60", + "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-v60", + "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-v60", + "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-v60", + "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-v60", + "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": "dc63de02ceffd87affd460283b26389250b0ec82", + "source_state": { + "base_revision": "dc63de02ceffd87affd460283b26389250b0ec82", + "checkout_state": "modified", + "implementation_digest": "9fd7f154b4cce99df55f4de367760560cd8e6f35ec24ebac4807294d921ca174", + "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/index.md b/docs/research/formal-semantic-validation/index.md index ed447984f..160e625be 100644 --- a/docs/research/formal-semantic-validation/index.md +++ b/docs/research/formal-semantic-validation/index.md @@ -336,7 +336,7 @@ 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 60.0.0, rejects unsupported future +Current validation requires explicit release 61.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 @@ -532,3 +532,9 @@ execution-authority admission from #1361. It binds the post-revert API-424 sourc 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. + +Release 61.0.0 is recorded in +[`execution-snapshot-v60.json`](execution-snapshot-v60.json) and +[`analysis-v60.json`](analysis-v60.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. diff --git a/docs/research/specification-coverage/analysis-v60.json b/docs/research/specification-coverage/analysis-v60.json new file mode 100644 index 000000000..81e235621 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v60.json @@ -0,0 +1,107 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-1359-v60", + "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-30T04:21:21.856964+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.", + "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.", + "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-v60", + "snapshot_sha256": "3036bb62cf3ba50bd06242b45f9c8dd6a10bfbd30bb5087b3f9fccbc4e14e628" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1359-v60.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1359-v60.json new file mode 100644 index 000000000..25bca3a6e --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1359-v60.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v60.json", + "analysis_sha256": "d39d519bd851af2c4f38742e8fca8feb22e6c3799a16ef006283806cbdd59b13", + "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": "71b5bb26c3a9b57aee4a61c60f626fcf370bea095000e07d17db5f5c018a8c3a" +} 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..4e2de4f37 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v60.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-30T04:21:21.856964+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": "dc63de02ceffd87affd460283b26389250b0ec82", + "snapshot_id": "raes-standardized-specification-coverage-issue-1359-v60", + "snapshot_revision": "60.0.0", + "source_state": { + "base_revision": "dc63de02ceffd87affd460283b26389250b0ec82", + "checkout_state": "modified", + "implementation_digest": "9fd7f154b4cce99df55f4de367760560cd8e6f35ec24ebac4807294d921ca174", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/index.md b/docs/research/specification-coverage/index.md index 8f1c34b55..f8bb75469 100644 --- a/docs/research/specification-coverage/index.md +++ b/docs/research/specification-coverage/index.md @@ -292,7 +292,7 @@ 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 59.0.0 and rejects duplicate or unsupported +Current validation requires release 60.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; @@ -474,3 +474,10 @@ execution-authority admission from #1361. It binds the post-revert API-424 sourc 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. + +Release 60.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-v60.json`](execution-snapshot-v60.json) and +[`analysis-v60.json`](analysis-v60.json). diff --git a/implementations/python/tests/test_formal_semantic_validation.py b/implementations/python/tests/test_formal_semantic_validation.py index e3db73280..9b72f1cd8 100644 --- a/implementations/python/tests/test_formal_semantic_validation.py +++ b/implementations/python/tests/test_formal_semantic_validation.py @@ -139,6 +139,7 @@ def test_atomic_release_index_validates_every_historical_bundle() -> None: "58.0.0", "59.0.0", "60.0.0", + "61.0.0", ] assert all(validate_release_bundle(REPO_ROOT, release) == [] for release in releases) @@ -147,10 +148,10 @@ 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"] == "60.0.0" + assert release.manifest["revision"] == "61.0.0" assert protocol["revision"] == "2.0.0" assert corpus["revision"] == "4.0.0" - assert snapshot["baseline"]["release_revision"] == "59.0.0" + assert snapshot["baseline"]["release_revision"] == "60.0.0" assert snapshot["deviations"] == [] assert validate_retest_bundle(REPO_ROOT, release, protocol, corpus, snapshot, analysis) == [] diff --git a/implementations/python/tests/test_issue_989_versioned_evidence.py b/implementations/python/tests/test_issue_989_versioned_evidence.py index d7cc224ff..446706140 100644 --- a/implementations/python/tests/test_issue_989_versioned_evidence.py +++ b/implementations/python/tests/test_issue_989_versioned_evidence.py @@ -230,7 +230,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"] == "60.0.0" + assert release.manifest["revision"] == "61.0.0" original = _retest.replay_case def changed_result(root, case): @@ -283,7 +283,7 @@ def test_specification_current_capture_does_not_accept_old_artifact_digest(artif from tools.check_specification_coverage import load_bundle, validate_bundle manifest, protocol, snapshot, analysis = copy_bundle(load_bundle, ROOT) - assert manifest["revision"] == "59.0.0" + assert manifest["revision"] == "60.0.0" snapshot = deepcopy(snapshot) artifact = next(a for a in snapshot["artifacts"] if a["artifact_id"] == artifact_id) artifact["sha256"] = old_digest @@ -512,6 +512,7 @@ def test_no_capture_can_be_silently_dropped(monkeypatch, family, removed): "58.0.0", "59.0.0", "60.0.0", + "61.0.0", ] if family == "formal" else [ @@ -575,6 +576,7 @@ def test_no_capture_can_be_silently_dropped(monkeypatch, family, removed): "57.0.0", "58.0.0", "59.0.0", + "60.0.0", ] ) revisions.pop(-1 if removed == "current" else 0) diff --git a/implementations/python/tests/test_specification_coverage.py b/implementations/python/tests/test_specification_coverage.py index cd9129b2c..4629ec949 100644 --- a/implementations/python/tests/test_specification_coverage.py +++ b/implementations/python/tests/test_specification_coverage.py @@ -53,7 +53,7 @@ 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", "19.0.0"} manifest, *_rest = copy_bundle(load_bundle, REPO_ROOT) - assert manifest["revision"] == "59.0.0" + assert manifest["revision"] == "60.0.0" def test_historical_failures_name_the_revision_specific_documents() -> None: diff --git a/tools/check_specification_coverage.py b/tools/check_specification_coverage.py index 481dc119a..325b1d2f4 100644 --- a/tools/check_specification_coverage.py +++ b/tools/check_specification_coverage.py @@ -157,12 +157,12 @@ def _load_bundle_index(repo_root: Path) -> list[tuple[str, dict[str, object]]]: "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"} + } | {"54.0.0", "55.0.0", "56.0.0", "57.0.0", "58.0.0", "59.0.0", "60.0.0"} if ( - dict(records)[current_path].get("revision") != "59.0.0" + dict(records)[current_path].get("revision") != "60.0.0" or {record.get("revision") for _, record in records} != supported_revisions ): - raise ValueError("coverage evidence requires the explicit current 59.0.0 release and supported history") + raise ValueError("coverage evidence requires the explicit current 60.0.0 release and supported history") return records diff --git a/tools/formal_semantic_validation/_baseline.py b/tools/formal_semantic_validation/_baseline.py index 6747bd701..0a0d65ea5 100644 --- a/tools/formal_semantic_validation/_baseline.py +++ b/tools/formal_semantic_validation/_baseline.py @@ -196,6 +196,7 @@ def _selected_baseline_manifest( "58.0.0", "59.0.0", "60.0.0", + "61.0.0", }: expected_corpus_path = "docs/research/formal-semantic-validation/corpus/manifest-v4.json" elif baseline_revision in _V3_CORPUS_REVISIONS: diff --git a/tools/formal_semantic_validation/_loading.py b/tools/formal_semantic_validation/_loading.py index f8d5b856c..182748ed2 100644 --- a/tools/formal_semantic_validation/_loading.py +++ b/tools/formal_semantic_validation/_loading.py @@ -75,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") != "60.0.0" or release.protocol.get("revision") != "2.0.0": - raise ValueError("the current formal evidence release must be the explicit 60.0.0 retest") + if release.manifest.get("revision") != "61.0.0" or release.protocol.get("revision") != "2.0.0": + raise ValueError("the current formal evidence release must be the explicit 61.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 index 4204db8c1..2f8e46087 100644 --- a/tools/formal_semantic_validation/_release_revisions.py +++ b/tools/formal_semantic_validation/_release_revisions.py @@ -53,9 +53,9 @@ "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"} +) | {"53.0.0", "54.0.0", "55.0.0", "56.0.0", "57.0.0", "58.0.0", "59.0.0", "60.0.0"} -_SUPPORTED_RETEST_REVISIONS = _HISTORICAL_RETEST_REVISIONS | {"60.0.0"} +_SUPPORTED_RETEST_REVISIONS = _HISTORICAL_RETEST_REVISIONS | {"61.0.0"} _SOURCE_BOUND_RETEST_REVISIONS = _SUPPORTED_RETEST_REVISIONS - {"3.0.0"} _RETEST_BASELINE_REVISIONS = { @@ -117,4 +117,5 @@ "58.0.0": "57.0.0", "59.0.0": "58.0.0", "60.0.0": "59.0.0", + "61.0.0": "60.0.0", } diff --git a/tools/formal_semantic_validation/_releases.py b/tools/formal_semantic_validation/_releases.py index b966ad1e1..8b894688d 100644 --- a/tools/formal_semantic_validation/_releases.py +++ b/tools/formal_semantic_validation/_releases.py @@ -160,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") == "60.0.0", + replay_current=manifest.get("revision") == "61.0.0", ) ) else: @@ -278,6 +278,7 @@ def _expected_corpus_revision(release_revision: object) -> str: "58.0.0", "59.0.0", "60.0.0", + "61.0.0", }: expected_corpus_revision = "4.0.0" return expected_corpus_revision @@ -305,7 +306,7 @@ def validate_retest_bundle( return [ _failure( "formal-validation-current-replay-required", - "only releases 3.0.0 through 59.0.0 can use integrated historical validation", + "only releases 3.0.0 through 60.0.0 can use integrated historical validation", snapshot_path, ) ] diff --git a/tools/formal_semantic_validation/_retest.py b/tools/formal_semantic_validation/_retest.py index 21100eeaf..ec92b1148 100644 --- a/tools/formal_semantic_validation/_retest.py +++ b/tools/formal_semantic_validation/_retest.py @@ -37,7 +37,7 @@ ) from tools.policy.common import PolicyFailure -_SOURCE_STATE_REVISIONS = frozenset(f"{revision}.0.0" for revision in range(4, 61)) +_SOURCE_STATE_REVISIONS = frozenset(f"{revision}.0.0" for revision in range(4, 62)) @dataclasses.dataclass(frozen=True) From 7c57e70c0435bd978605f84a1f5a024f78fde774 Mon Sep 17 00:00:00 2001 From: Brad Edwards Date: Wed, 30 Sep 2026 06:37:11 +0200 Subject: [PATCH 11/11] fix(deps): upgrade pyjwt to 2.15.1 for ten published advisories --- .../formal-semantic-validation/analysis-v60.json | 2 +- .../bundles/retest-v60.json | 4 ++-- .../execution-snapshot-v60.json | 4 ++-- .../specification-coverage/analysis-v60.json | 4 ++-- ...ized-specification-coverage-issue-1359-v60.json | 4 ++-- .../execution-snapshot-v60.json | 4 ++-- implementations/python/uv.lock | 6 +++--- .../tooling/python/smoke/linux-arm64-cp314.txt | 2 +- .../linux-arm64-cp314.wheelhouse-manifest.json | 14 +++++++------- .../tooling/python/smoke/linux-x86_64-cp311.txt | 2 +- .../linux-x86_64-cp311.wheelhouse-manifest.json | 14 +++++++------- .../tooling/python/smoke/linux-x86_64-cp312.txt | 2 +- .../linux-x86_64-cp312.wheelhouse-manifest.json | 14 +++++++------- .../tooling/python/smoke/linux-x86_64-cp313.txt | 2 +- .../linux-x86_64-cp313.wheelhouse-manifest.json | 14 +++++++------- .../tooling/python/smoke/linux-x86_64-cp314.txt | 2 +- .../linux-x86_64-cp314.wheelhouse-manifest.json | 14 +++++++------- .../tooling/python/smoke/macos-arm64-cp314.txt | 2 +- .../macos-arm64-cp314.wheelhouse-manifest.json | 14 +++++++------- 19 files changed, 62 insertions(+), 62 deletions(-) diff --git a/docs/research/formal-semantic-validation/analysis-v60.json b/docs/research/formal-semantic-validation/analysis-v60.json index 96c924d33..c6b7f457b 100644 --- a/docs/research/formal-semantic-validation/analysis-v60.json +++ b/docs/research/formal-semantic-validation/analysis-v60.json @@ -124,7 +124,7 @@ "corpus_revision": "4.0.0", "evidence_status": "partial", "execution_id": "issue-1359-execution-v60", - "generated_at": "2026-09-30T04:21:21.856964+00:00", + "generated_at": "2026-09-30T04:33:15.753826+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.", diff --git a/docs/research/formal-semantic-validation/bundles/retest-v60.json b/docs/research/formal-semantic-validation/bundles/retest-v60.json index 5284996ea..ce615bbcd 100644 --- a/docs/research/formal-semantic-validation/bundles/retest-v60.json +++ b/docs/research/formal-semantic-validation/bundles/retest-v60.json @@ -1,6 +1,6 @@ { "analysis_path": "docs/research/formal-semantic-validation/analysis-v60.json", - "analysis_sha256": "49d1cc85df03b51778c1f3bdfad74630d661d3958ec043cb5db14c6120dc6cfb", + "analysis_sha256": "231ce937255f4b708d68376deddbbda95e1a703ea46ffa0895398d99a13f4cc9", "artifacts": [ { "artifact_id": "finite-domain-satisfiable-v2-input", @@ -118,5 +118,5 @@ "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", "revision": "61.0.0", "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v60.json", - "snapshot_sha256": "c5e721a5b44678af012ced61248651c9ef5199265faf00cfd015da7b65c838aa" + "snapshot_sha256": "4f04be28b85605db3f70fd6ed93fe309de292504e432e06f341056f8169f108d" } diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v60.json b/docs/research/formal-semantic-validation/execution-snapshot-v60.json index 69d72a009..4807f4a31 100644 --- a/docs/research/formal-semantic-validation/execution-snapshot-v60.json +++ b/docs/research/formal-semantic-validation/execution-snapshot-v60.json @@ -5,7 +5,7 @@ "release_revision": "60.0.0", "release_sha256": "08e87e3e1531dd445d88227b2fb541f9c8c2846f621d29275e5f7fedca120a3b" }, - "captured_at": "2026-09-30T04:21:21.856964+00:00", + "captured_at": "2026-09-30T04:33:15.753826+00:00", "commands": [ { "argv": [ @@ -640,7 +640,7 @@ "source_state": { "base_revision": "dc63de02ceffd87affd460283b26389250b0ec82", "checkout_state": "modified", - "implementation_digest": "9fd7f154b4cce99df55f4de367760560cd8e6f35ec24ebac4807294d921ca174", + "implementation_digest": "1166e4c4b6f8bd93a58f20d27b46559f4e0fe1f59f620fc06b5c2f4b3b2c88d0", "profile": "python-reference-source/v2" }, "versions": { diff --git a/docs/research/specification-coverage/analysis-v60.json b/docs/research/specification-coverage/analysis-v60.json index 81e235621..7b6aa4f9c 100644 --- a/docs/research/specification-coverage/analysis-v60.json +++ b/docs/research/specification-coverage/analysis-v60.json @@ -39,7 +39,7 @@ }, "evidence_status": "partial", "execution_status": "complete", - "generated_at": "2026-09-30T04:21:21.856964+00:00", + "generated_at": "2026-09-30T04:33:15.753826+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.", @@ -103,5 +103,5 @@ } ], "snapshot_id": "raes-standardized-specification-coverage-issue-1359-v60", - "snapshot_sha256": "3036bb62cf3ba50bd06242b45f9c8dd6a10bfbd30bb5087b3f9fccbc4e14e628" + "snapshot_sha256": "c1feb1923e1b13a1c50f473040877101ec4b75a5360541871292d4ace029b80e" } diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1359-v60.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1359-v60.json index 25bca3a6e..1546d331c 100644 --- a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1359-v60.json +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1359-v60.json @@ -1,10 +1,10 @@ { "analysis_path": "docs/research/specification-coverage/analysis-v60.json", - "analysis_sha256": "d39d519bd851af2c4f38742e8fca8feb22e6c3799a16ef006283806cbdd59b13", + "analysis_sha256": "bae7cf9b95311d16d904f8fc9ecfffbb0b86b77b9a086a46aa06b4f061586976", "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": "71b5bb26c3a9b57aee4a61c60f626fcf370bea095000e07d17db5f5c018a8c3a" + "snapshot_sha256": "e3fd7496ec6f53d7571cce08f04f0234a067ed8ee52ebf51a6ddbf465861f46e" } diff --git a/docs/research/specification-coverage/execution-snapshot-v60.json b/docs/research/specification-coverage/execution-snapshot-v60.json index 4e2de4f37..794085001 100644 --- a/docs/research/specification-coverage/execution-snapshot-v60.json +++ b/docs/research/specification-coverage/execution-snapshot-v60.json @@ -47,7 +47,7 @@ "release_revision": "1.1.0", "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" }, - "captured_at": "2026-09-30T04:21:21.856964+00:00", + "captured_at": "2026-09-30T04:33:15.753826+00:00", "concept_results": [ { "backend_support": "not-evaluated", @@ -696,7 +696,7 @@ "source_state": { "base_revision": "dc63de02ceffd87affd460283b26389250b0ec82", "checkout_state": "modified", - "implementation_digest": "9fd7f154b4cce99df55f4de367760560cd8e6f35ec24ebac4807294d921ca174", + "implementation_digest": "1166e4c4b6f8bd93a58f20d27b46559f4e0fe1f59f620fc06b5c2f4b3b2c88d0", "profile": "python-reference-source/v2" } } diff --git a/implementations/python/uv.lock b/implementations/python/uv.lock index 4898369a6..2e2181fa1 100644 --- a/implementations/python/uv.lock +++ b/implementations/python/uv.lock @@ -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] diff --git a/implementations/tooling/python/smoke/linux-arm64-cp314.txt b/implementations/tooling/python/smoke/linux-arm64-cp314.txt index f70edd674..4a81563bb 100644 --- a/implementations/tooling/python/smoke/linux-arm64-cp314.txt +++ b/implementations/tooling/python/smoke/linux-arm64-cp314.txt @@ -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 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 cba1adeee..e77cf3de7 100644 --- a/implementations/tooling/python/smoke/linux-arm64-cp314.wheelhouse-manifest.json +++ b/implementations/tooling/python/smoke/linux-arm64-cp314.wheelhouse-manifest.json @@ -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", @@ -730,8 +730,8 @@ } ], "lock_path": "implementations/python/uv.lock", - "lock_sha256": "089c252ebfadd73c474b8e7606b67b0e10abfd02be113b90c1bd6fc91da720dd", + "lock_sha256": "6cab336d0029e1225206e834bdd63e40a160d9068eda844a982133cbb562a13e", "python_closure_profile_id": "public-linux-arm64-cp314-all-extras", - "requirements_sha256": "80683a45b71cd5a285abcd6115fd60b3f2bab31b61e664363346f255862c8047", + "requirements_sha256": "c862dcb2139f6f952e9e738152d537d9a8fdf68a70a9894be611e15073a35e90", "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 8feda8e8d..e3555f279 100644 --- a/implementations/tooling/python/smoke/linux-x86_64-cp311.txt +++ b/implementations/tooling/python/smoke/linux-x86_64-cp311.txt @@ -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 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 251924e9b..244fb5267 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 @@ -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", @@ -730,8 +730,8 @@ } ], "lock_path": "implementations/python/uv.lock", - "lock_sha256": "089c252ebfadd73c474b8e7606b67b0e10abfd02be113b90c1bd6fc91da720dd", + "lock_sha256": "6cab336d0029e1225206e834bdd63e40a160d9068eda844a982133cbb562a13e", "python_closure_profile_id": "public-linux-x86_64-cp311-all-extras", - "requirements_sha256": "d3afe72bb25fb802dec0858de0af71c7129fbabb081ea334f18a4ce5c73b60ad", + "requirements_sha256": "d0520216963b53c4cf112b0dc06261edae39b9a775106b84d1e9e16b0d55dda4", "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 9955645fd..6d1ed61d1 100644 --- a/implementations/tooling/python/smoke/linux-x86_64-cp312.txt +++ b/implementations/tooling/python/smoke/linux-x86_64-cp312.txt @@ -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 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 7d482401d..167b2b679 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 @@ -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", @@ -730,8 +730,8 @@ } ], "lock_path": "implementations/python/uv.lock", - "lock_sha256": "089c252ebfadd73c474b8e7606b67b0e10abfd02be113b90c1bd6fc91da720dd", + "lock_sha256": "6cab336d0029e1225206e834bdd63e40a160d9068eda844a982133cbb562a13e", "python_closure_profile_id": "public-linux-x86_64-cp312-all-extras", - "requirements_sha256": "7390076b9b60eacaa7453e4a8259a8478760a1008140fb1100900f145146d987", + "requirements_sha256": "81943e86bc26cd7748596dc6d3cc7ce1ac08f767a5b78e6da2508b46af602255", "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 15616a7cb..76d654d95 100644 --- a/implementations/tooling/python/smoke/linux-x86_64-cp313.txt +++ b/implementations/tooling/python/smoke/linux-x86_64-cp313.txt @@ -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 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 d2c314ee6..92750c6eb 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 @@ -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", @@ -730,8 +730,8 @@ } ], "lock_path": "implementations/python/uv.lock", - "lock_sha256": "089c252ebfadd73c474b8e7606b67b0e10abfd02be113b90c1bd6fc91da720dd", + "lock_sha256": "6cab336d0029e1225206e834bdd63e40a160d9068eda844a982133cbb562a13e", "python_closure_profile_id": "public-linux-x86_64-cp313-all-extras", - "requirements_sha256": "972e2c097f06d633c5b37ca38f2649180708f8c1e2378ff4c32b45c44a519796", + "requirements_sha256": "2f65240239db3cf75b4d32d635ba9a94e90e991e36eb4519236859dbb3df3ce1", "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 12d3765c7..5de3ee6f0 100644 --- a/implementations/tooling/python/smoke/linux-x86_64-cp314.txt +++ b/implementations/tooling/python/smoke/linux-x86_64-cp314.txt @@ -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 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 d74ba0e60..771690a2f 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 @@ -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", @@ -730,8 +730,8 @@ } ], "lock_path": "implementations/python/uv.lock", - "lock_sha256": "089c252ebfadd73c474b8e7606b67b0e10abfd02be113b90c1bd6fc91da720dd", + "lock_sha256": "6cab336d0029e1225206e834bdd63e40a160d9068eda844a982133cbb562a13e", "python_closure_profile_id": "public-linux-x86_64-cp314-all-extras", - "requirements_sha256": "114cfc5b65e331634aa5e5a12cc0a9c230c26decb3e4eba5263c2653848f3e30", + "requirements_sha256": "2adffe30f16f4d2c7622aa5034be54de495b501a221162d61b83570c349ce250", "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 cefcca5b6..9cc085e4f 100644 --- a/implementations/tooling/python/smoke/macos-arm64-cp314.txt +++ b/implementations/tooling/python/smoke/macos-arm64-cp314.txt @@ -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 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 863588c3f..77d64f60f 100644 --- a/implementations/tooling/python/smoke/macos-arm64-cp314.wheelhouse-manifest.json +++ b/implementations/tooling/python/smoke/macos-arm64-cp314.wheelhouse-manifest.json @@ -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", @@ -730,8 +730,8 @@ } ], "lock_path": "implementations/python/uv.lock", - "lock_sha256": "089c252ebfadd73c474b8e7606b67b0e10abfd02be113b90c1bd6fc91da720dd", + "lock_sha256": "6cab336d0029e1225206e834bdd63e40a160d9068eda844a982133cbb562a13e", "python_closure_profile_id": "public-macos-arm64-cp314-all-extras", - "requirements_sha256": "3a56212b27baace71c892671e3b0c94c2cbab8953b45f14b673ffea4c8f7bec3", + "requirements_sha256": "bb4a588a4de0a4c673706f508ee8403c9056a9bad84567633059529e14eb7c47", "schema_version": "raes-python-wheelhouse-manifest/v1" }