Skip to content

fix: realize relayed provisioning plans from their published operations - #1433

Open
doublewhy wants to merge 2 commits into
devfrom
1425-relay-plan-parity
Open

doublewhy wants to merge 2 commits into
devfrom
1425-relay-plan-parity

Conversation

@doublewhy

@doublewhy doublewhy commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Summary

The reference and libvirt interpreters built their realization only from ProvisioningPlan.resources. POST /operations/provisioning converts the published ProvisioningPlanModel with control_plane_api_models._provisioning_plan, and the published model carries operations and no resources map. On dev at 3512210, a planner-authorized plan submitted over HTTP to the reference backend therefore ended SUCCEEDED and committed provision.network.lab and provision.node.web, while the in-process driver realized nothing. The same plan applied directly through RuntimeManager.apply realizes both.

The map is also outside runtime_plan_digest. An in-process plan whose resources map had been rewritten kept its digest and planner authorization, and the driver realized the rewritten node image (attacker/backdoor:latest in the regression test).

This PR adds planned_provisioning_resources(plan) to raes_contracts.plan_projection. It returns one PlannedResource per non-delete operation, and falls back to resources only for an operation-free plan. Both interpreters iterate it instead of plan.resources. Because this changes source bound by the research evidence, both captures are republished at the next free releases (specification coverage 69.0.0, formal validation 70.0.0). Retained outcomes, classifications and claim limits are unchanged.

Requirement UIDs

Related Issues

Closes #1425

ADR Impact

  • None. No ADR text changes, and the published plan schema and plan digest are unchanged. ADR-063 describes interpret_provisioning_plan(plan) as mapping node, network and placement resources without naming the map it reads, so it still holds.

Changes

  • implementations/python/packages/raes_contracts/plan_projection.py: new planned_provisioning_resources(plan), next to runtime_plan_digest, the projection whose content it reads. Each non-delete operation yields a PlannedResource with the operation's address, type, payload, dependencies and profile bindings in the provisioning domain. A plan with no operations returns resources, so the pure interpreters still accept the operation-free plans that existing realization tests build. Apply paths drive no resource without an active operation. The module docstring now names this backend-facing view.
  • implementations/python/packages/raes_reference_backend/realization.py (interpret_provisioning_plan) and implementations/python/packages/raes_backend_libvirt/realization/_plan.py (_collect_supported_resources) iterate planned_provisioning_resources(plan) instead of plan.resources.
  • implementations/python/tests/test_issue_1425_relay_plan_parity.py (new):
    • The HTTP relay of a registered plan realizes every committed address on the reference driver.
    • Direct and relayed interpretation are equal for both interpreters, and non-empty.
    • A rewritten resources map keeps the plan digest but does not change the realized image.
  • Research evidence republished, because tools/research_evidence.py implementation_digest() hashes every package .py:
    • Specification-coverage release 69.0.0 (execution-snapshot-v69.json, analysis-v69.json, issue-1425 bundle) replays the retained matrix against this branch's source.
    • Formal-validation release 70.0.0 (execution-snapshot-v70.json, analysis-v70.json, retest-v70.json) replays the retained formal cases with baseline 69.0.0. The snapshot records no deviation (deviations: []).
    • Revision pins are advanced in tools/check_specification_coverage.py, tools/formal_semantic_validation/, and the three evidence test modules. Both research indexes record the new releases.

Reviewer notes:

  • For planner output, the non-delete operations already carry everything the resources map holds. raes_processor/planner/operations._build_provisioning_plan builds resources and the operations from the same provisioning resources, one operation per resource (_ordered_apply_ops). raes_runtime/backend_preparation._selected_plan already rebuilds resources from non-delete operations. Direct execution therefore realizes the same networks, containers, domains and placements as before.
  • Output order is unchanged apart from diagnostics. The reference interpreter sorts networks and placements by address and orders containers with _order_containers, which visits addresses in sorted order. Libvirt iterates resources in address order. Only the reference interpreter's diagnostics now follow operation order.
  • Other readers of resources are unchanged. raes_backend_libvirt/capability_envelope._materialized_payloads and raes_backend_protocols/domain_topology._materialized_resources read resources and then every non-delete operation. raes_contracts/account_materialization._mailbox_inventory reads only resources, so a relayed mailbox plan is still refused with reference-backend.mailbox-unsupported. That fails closed and is not part of this false success.
  • Known residuals of the same root cause, out of scope here: in an in-process plan, a rewritten resources map still feeds _mailbox_inventory, and _mailbox_request deep-copies that mailbox record into AccountMailboxMaterialization (the credential still comes from the digest-bound operation), so the tamper fix covers the two interpreters only. HTTP-relayed orchestration and evaluation plans always arrive with resources={}, so the reference orchestrator (raes_reference_backend/orchestrator.py:63) and evaluator (raes_reference_backend/evaluator.py:58) report running: False.

Test Plan

  • Unit tests pass
  • Integration tests pass if applicable
  • Full completion suite required in CI before merge
  • No coverage regression

uv run --project implementations/python --frozen --all-extras python -m pytest implementations/python/tests/test_issue_1425_relay_plan_parity.py -q -p no:cacheprovider passed (4 passed). With the three source files reverted to origin/dev (git checkout origin/dev -- on plan_projection.py, realization.py and _plan.py), the same command failed all 4 cases. The HTTP case realized frozenset(), both parity cases found an empty relayed realization, and the tampered case realized attacker/backdoor:latest. The operation branch of planned_provisioning_resources runs in every case, and its operation-free branch runs in the existing realization tests below. A direct RuntimeManager.apply of the issue's scenario on the in-process driver realized provision.network.lab and provision.node.web, both on dev's source and on this branch.

The modules named in the issue, the new module, and test_libvirt_backend_guest_certified.py passed (211 passed): test_reference_backend_realization.py, test_libvirt_backend_realization.py, test_reference_backend_provisioner.py, test_libvirt_backend_provisioner.py, test_domain_controller_placement.py and test_runtime_control_plane_api.py. With -m integration all 211 are deselected, because none is integration-marked.

The 70 tests/*.py files that reference either package passed: 1376 passed, 6 deselected. They are the output of grep -l 'raes_reference_backend\|raes_backend_libvirt' implementations/python/tests/*.py: 67 test modules plus libvirt_participant_proof.py, libvirt_conformance_fixtures.py and libvirt_participant_fixtures.py. With -m integration, 2 passed and 2 skipped. The passes are test_repo_policy_tools.py::test_make_policy_delegates_requirement_context_to_the_shared_nox_gate and test_repo_policy_tools.py::test_default_structural_policy_runner_executes_rego. The two real-libvirt certification modules skip because RAES_REAL_LIBVIRT_URI is unset. test_reference_backend_docker_integration.py carries only the docker marker and was not run locally.

The 16 test modules whose source mentions plan_projection and are in neither set above passed (241 passed). They are the output of grep -l plan_projection implementations/python/tests/test_*.py, less the two sets above: test_plan_projection.py, test_issue_673_account_credential_bindings.py, test_issue_1187_control_plane_security_conformance.py, test_issue_1200_mixed_runtime_constraints.py, test_issue_1204_collection_lifecycle.py, test_issue_1204_nested_domains.py, test_issue_1204_recursive_carriage.py, test_issue_1204_recursive_defaults.py, test_issue_1204_resource_collections.py, test_issue_1212_runtime_boundaries.py, test_issue_1241_inspection_plans.py, test_issue_1241_materialization_context.py, test_issue_1241_materialization_runtime.py, test_issue_1242_review_regressions.py, test_issue_1361_execution_policy.py and test_issue_1361_policy_admission.py. With -m integration all 241 are deselected.

After the republish, tools/check_specification_coverage.py and tools/check_formal_semantic_validation.py passed under uv run --project implementations/python --frozen --all-extras. The three evidence test modules passed with default markers (209 passed, 19 deselected) and with -m integration (19 passed, 209 deselected). nox -s verify-fast-feedback -- --base-rev origin/dev passed every stage except two skips: requirement governance (--skip-requirement) and YAML syntax (no YAML files changed). It ran the four directly changed test modules (213 passed, 19 deselected). nox -s lint passed.

On 57f309e, all 32 PR checks passed and deploy was skipped. In CI run 37936049355, job canonical / integration reported 226 passed and 2 skipped (the two real-libvirt certification tests), and test_repo_policy_tools.py::test_default_structural_policy_runner_executes_rego PASSED. Job integration-docker ran both tests in test_reference_backend_docker_integration.py (2 passed), and canonical / coverage-reduce passed. The SonarCloud quality gate is OK for 57f309e: new-code coverage 100.0%, duplication 0.0%, 0 new issues.

Ground Control Checks

  • Repository policy command passes
  • Pre-push code review and test-quality review completed, or not run for this lane

make policy passed (requirement governance skipped by --skip-requirement). No pre-push Codex or test-quality review was run for this lane.

Traceability

  • IMPLEMENTS: implementations/python/packages/raes_contracts/plan_projection.py, implementations/python/packages/raes_reference_backend/realization.py, implementations/python/packages/raes_backend_libvirt/realization/_plan.py
  • TESTS: implementations/python/tests/test_issue_1425_relay_plan_parity.py
  • Lineage only, with no requirement claim: Carry resolved realization posture through the backend-facing plan #1067 ("Carry resolved realization posture through the backend-facing plan") closed through feat: carry resolved realization authority through plans #1081. Its preflight, docs/decisions/issue-1067-resolved-realization-posture-handoff-preflight.md, says "Reference and libvirt must consume the same total plan lookup" (line 281), requires the runtime-manager and authenticated HTTP paths to "yield the same non-approximation diagnostic" (lines 285-286), and says "Do not preserve only in-process behavior" (line 325). Those rules govern the realization-posture handoff. This PR applies the same parity to the desired resources the interpreters realize.

Checklist

  • Code follows the project coding standards
  • FM level classified if semantic change: FM2, a portability rule between layers ("Backend Contract Semantics" in docs/explain/reference/coding-standards.md). Invariants: (1) when a provisioning plan has operations, the reference and libvirt interpreters read desired resources only from its non-delete operations, which runtime_plan_digest covers, and they read resources only for an operation-free plan; (2) interpreting a plan relayed through its published model equals interpreting it directly. Artifacts: this invariant list and the unit tests in test_issue_1425_relay_plan_parity.py. Its parametrized test_relayed_and_direct_plans_interpret_identically is the differential test across both interpreters, and it relays through the published ProvisioningPlanModel. No formal model is added. No executable or runtime adoption is claimed.
  • Published contract schemas regenerated if models changed: no model or schema changed.
  • PR title is a Conventional Commit (release-please derives the version and CHANGELOG.md from it)
  • Architectural docs updated if applicable: not applicable. Outside docs/decisions and docs/research, no document mentions interpret_provisioning_plan or the resources map, and ADR-063 (see ADR Impact) does not name the map the interpreters read.

The reference and libvirt interpreters built their realization only from ProvisioningPlan.resources. The authenticated HTTP route carries the published plan model, which has no resources map, so a relayed plan committed every planned address as SUCCEEDED while the driver realized nothing. The map is also outside runtime_plan_digest, so a rewritten map changed what was realized without changing the planner authorization.

planned_provisioning_resources(plan) in raes_contracts.plan_projection returns one PlannedResource per non-delete operation, falling back to resources only for operation-free plans, and both interpreters iterate it.
Specification-coverage release 69.0.0 and formal-validation release 70.0.0 bind the current implementation digest; outcomes and claim limits are unchanged.
@doublewhy
doublewhy force-pushed the 1425-relay-plan-parity branch from 708e34b to 57f309e Compare October 9, 2026 13:19
@doublewhy
doublewhy marked this pull request as ready for review October 9, 2026 15:24

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant