From 20013e84327c632bdef5fb2afa8f09bf80f67451 Mon Sep 17 00:00:00 2001 From: Doga Gursoy Date: Mon, 22 Jun 2026 14:46:47 +0300 Subject: [PATCH] refactor(operation): carve _recipe_expansion/ + _pseudoaxis/ subpackages The Operation BC root had drifted to 12 private _*.py modules, past the ~10-file "revisit" threshold in docs/reference/layout.md (the 2nd BC to cross it; the equipment _bodies/ + _pidinst/ carve is the n=1 precedent). This groups two cohesive clusters into private subpackages whose __init__ re-exports the public surface, so consumers import from the package: - _recipe_expansion/ <- _expand.py (was _recipe_expansion.py), _replay.py (was _recipe_replay.py), _resolved_steps_replay.py. The recipe step-resolution + replay/verify cluster. - _pseudoaxis/ <- _evaluator.py (was _pseudoaxis_evaluator.py), _expander.py (was _pseudoaxis_expander.py). The virtual-axis resolution cluster. Root drops from 12 to 7 private modules + 2 subpackages. Pure, behavior-preserving file reorganization: the moved modules' content is byte-identical except one intra-cluster import line each (rewritten to the sibling submodule), all 56... the consumer import sites are updated to the package path, and a path-coupled arch test (test_recipe_step_variants_match_step_union) is retargeted to the _expand submodule where the private _expand_step dispatch helper now lives. Submodules drop the redundant package prefix (_pseudoaxis/_evaluator, not _pseudoaxis/_pseudoaxis_evaluator), matching the locked _pidinst/ precedent. No behavior change: operation unit tests (881) + full arch (26916) + full unit tier (10466) pass; gate review confirmed all imports intact (no P0/P1), behavior preserved, and naming-r3 clean. Co-Authored-By: Claude Opus 4.8 --- .../cora/operation/_conduct_preparation.py | 6 +-- apps/api/src/cora/operation/_conduct_wire.py | 2 +- .../cora/operation/_pseudoaxis/__init__.py | 32 +++++++++++++ .../_evaluator.py} | 0 .../_expander.py} | 2 +- .../operation/_recipe_expansion/__init__.py | 48 +++++++++++++++++++ .../_expand.py} | 0 .../_replay.py} | 2 +- .../_resolved_steps_replay.py | 0 .../adapters/in_memory_recipe_expander.py | 4 +- .../features/reconduct_procedure/handler.py | 2 +- .../cora/operation/ports/recipe_expander.py | 2 +- ...t_recipe_step_variants_match_step_union.py | 6 +-- .../operation/test_pseudoaxis_evaluator.py | 2 +- .../operation/test_pseudoaxis_expander.py | 2 +- .../unit/operation/test_recipe_replay.py | 6 +-- 16 files changed, 98 insertions(+), 18 deletions(-) create mode 100644 apps/api/src/cora/operation/_pseudoaxis/__init__.py rename apps/api/src/cora/operation/{_pseudoaxis_evaluator.py => _pseudoaxis/_evaluator.py} (100%) rename apps/api/src/cora/operation/{_pseudoaxis_expander.py => _pseudoaxis/_expander.py} (99%) create mode 100644 apps/api/src/cora/operation/_recipe_expansion/__init__.py rename apps/api/src/cora/operation/{_recipe_expansion.py => _recipe_expansion/_expand.py} (100%) rename apps/api/src/cora/operation/{_recipe_replay.py => _recipe_expansion/_replay.py} (98%) rename apps/api/src/cora/operation/{ => _recipe_expansion}/_resolved_steps_replay.py (100%) diff --git a/apps/api/src/cora/operation/_conduct_preparation.py b/apps/api/src/cora/operation/_conduct_preparation.py index c4368723dd6..af99e7a92d3 100644 --- a/apps/api/src/cora/operation/_conduct_preparation.py +++ b/apps/api/src/cora/operation/_conduct_preparation.py @@ -14,7 +14,7 @@ A slice cannot import a sibling slice (the cross-slice-independence fitness), so this BC-level module owns the shared pipeline, mirroring `_conduct_wire` -(shared HTTP/MCP shapes) and `_resolved_steps_replay` (the resume-side read). +(shared HTTP/MCP shapes) and `_recipe_expansion/_resolved_steps_replay` (the resume-side read). The pin is emitted inline rather than via a dedicated command slice: `ResolvedStepsRecorded` is an internal provenance event with no operator entry point, exactly like `RecipeExpansionRecorded`. @@ -29,7 +29,7 @@ from cora.infrastructure.kernel import Kernel from cora.infrastructure.ports import EventStore from cora.infrastructure.ports.event_store import StoredEvent -from cora.operation._recipe_replay import ( +from cora.operation._recipe_expansion import ( find_recipe_expansion_record, pins_from_payload, verify_bindings_hash, @@ -58,7 +58,7 @@ from cora.run.aggregates.run import RunNotFoundError, load_run if TYPE_CHECKING: - from cora.operation._pseudoaxis_expander import ConstituentResolver + from cora.operation._pseudoaxis import ConstituentResolver def decide_resolved_steps_recorded( diff --git a/apps/api/src/cora/operation/_conduct_wire.py b/apps/api/src/cora/operation/_conduct_wire.py index 9f542a86c9e..d9689a0080e 100644 --- a/apps/api/src/cora/operation/_conduct_wire.py +++ b/apps/api/src/cora/operation/_conduct_wire.py @@ -5,7 +5,7 @@ those wire types + converters so both slices reuse them. A slice cannot import a sibling slice (the cross-slice-independence fitness), so the shared seam lives here, outside `features/`, exactly like the resolved-steps replay -helper (`_resolved_steps_replay`) and preparation pipeline +helper (`_recipe_expansion/_resolved_steps_replay`) and preparation pipeline (`_conduct_preparation`). The Conductor's `Step = SetpointStep | ActionStep | CheckStep` and diff --git a/apps/api/src/cora/operation/_pseudoaxis/__init__.py b/apps/api/src/cora/operation/_pseudoaxis/__init__.py new file mode 100644 index 00000000000..2cc04ab8b89 --- /dev/null +++ b/apps/api/src/cora/operation/_pseudoaxis/__init__.py @@ -0,0 +1,32 @@ +"""PseudoAxis resolution subpackage for the Operation BC. + +Carved from the BC root once the private-module count crossed the ~10-file +threshold (see docs/reference/layout.md; the equipment `_bodies/` / `_pidinst/` +carve is the precedent). Groups the two private modules that resolve a virtual +(pseudo) axis into its real constituent axes at conduct time: + + - `_evaluator`: `resolve_pseudoaxis_command` -- map one pseudo-axis setpoint to + the resolved constituent setpoints (`ResolvedSetpoints`). + - `_expander`: `expand_pseudoaxis_steps` -- rewrite a virtual-axis `SetpointStep` + into N sequential constituent `SetpointStep`s before the Conductor walks them, + via an injected `ConstituentResolver`. + +Re-exports the public surface so consumers import from the package +(`from cora.operation._pseudoaxis import expand_pseudoaxis_steps, ConstituentResolver`). +""" + +from cora.operation._pseudoaxis._evaluator import ( + ResolvedSetpoints, + resolve_pseudoaxis_command, +) +from cora.operation._pseudoaxis._expander import ( + ConstituentResolver, + expand_pseudoaxis_steps, +) + +__all__ = [ + "ConstituentResolver", + "ResolvedSetpoints", + "expand_pseudoaxis_steps", + "resolve_pseudoaxis_command", +] diff --git a/apps/api/src/cora/operation/_pseudoaxis_evaluator.py b/apps/api/src/cora/operation/_pseudoaxis/_evaluator.py similarity index 100% rename from apps/api/src/cora/operation/_pseudoaxis_evaluator.py rename to apps/api/src/cora/operation/_pseudoaxis/_evaluator.py diff --git a/apps/api/src/cora/operation/_pseudoaxis_expander.py b/apps/api/src/cora/operation/_pseudoaxis/_expander.py similarity index 99% rename from apps/api/src/cora/operation/_pseudoaxis_expander.py rename to apps/api/src/cora/operation/_pseudoaxis/_expander.py index 45628a9d82a..7cf297cd218 100644 --- a/apps/api/src/cora/operation/_pseudoaxis_expander.py +++ b/apps/api/src/cora/operation/_pseudoaxis/_expander.py @@ -58,7 +58,7 @@ from typing import TYPE_CHECKING from uuid import UUID -from cora.operation._pseudoaxis_evaluator import resolve_pseudoaxis_command +from cora.operation._pseudoaxis._evaluator import resolve_pseudoaxis_command from cora.operation.conductor import ( SetpointStep, Step, diff --git a/apps/api/src/cora/operation/_recipe_expansion/__init__.py b/apps/api/src/cora/operation/_recipe_expansion/__init__.py new file mode 100644 index 00000000000..6262904c510 --- /dev/null +++ b/apps/api/src/cora/operation/_recipe_expansion/__init__.py @@ -0,0 +1,48 @@ +"""Recipe step-resolution subpackage for the Operation BC. + +Carved from the BC root once the private-module count crossed the ~10-file +threshold (see docs/reference/layout.md; the equipment `_bodies/` / `_pidinst/` +carve is the precedent). Groups the three private modules that turn a Recipe's +parameterized step tuple into the concrete `Step` list a Procedure conducts, and +that replay / verify that resolution: + + - `_expand`: the pure `expand(steps, bindings) -> Step list` substitution kernel + (+ `steps_to_wire` / `canonical_json_bytes` for provenance hashing). + - `_replay`: locate + verify the genesis `RecipeExpansionRecorded` provenance + on the `conduct_procedure` path (re-expand and compare pinned hashes). + - `_resolved_steps_replay`: locate the pinned `ResolvedStepsRecorded` provenance + on the `reconduct_procedure` (resume) path. + +Re-exports the public surface so consumers import from the package +(`from cora.operation._recipe_expansion import expand, find_recipe_expansion_record`). +""" + +from cora.operation._recipe_expansion._expand import ( + canonical_json_bytes, + expand, + steps_to_wire, +) +from cora.operation._recipe_expansion._replay import ( + MismatchField, + RecipeExpansionPins, + find_recipe_expansion_record, + pins_from_payload, + verify_bindings_hash, + verify_steps_hash, +) +from cora.operation._recipe_expansion._resolved_steps_replay import ( + find_resolved_steps_record, +) + +__all__ = [ + "MismatchField", + "RecipeExpansionPins", + "canonical_json_bytes", + "expand", + "find_recipe_expansion_record", + "find_resolved_steps_record", + "pins_from_payload", + "steps_to_wire", + "verify_bindings_hash", + "verify_steps_hash", +] diff --git a/apps/api/src/cora/operation/_recipe_expansion.py b/apps/api/src/cora/operation/_recipe_expansion/_expand.py similarity index 100% rename from apps/api/src/cora/operation/_recipe_expansion.py rename to apps/api/src/cora/operation/_recipe_expansion/_expand.py diff --git a/apps/api/src/cora/operation/_recipe_replay.py b/apps/api/src/cora/operation/_recipe_expansion/_replay.py similarity index 98% rename from apps/api/src/cora/operation/_recipe_replay.py rename to apps/api/src/cora/operation/_recipe_expansion/_replay.py index 31448cf3dbb..14bd64666cc 100644 --- a/apps/api/src/cora/operation/_recipe_replay.py +++ b/apps/api/src/cora/operation/_recipe_expansion/_replay.py @@ -24,7 +24,7 @@ from uuid import UUID from cora.infrastructure.ports.event_store import StoredEvent -from cora.operation._recipe_expansion import steps_to_wire +from cora.operation._recipe_expansion._expand import steps_to_wire from cora.operation.aggregates.procedure import ( RecipeExpansionRecordNotFoundError, RecipeExpansionReplayMismatchError, diff --git a/apps/api/src/cora/operation/_resolved_steps_replay.py b/apps/api/src/cora/operation/_recipe_expansion/_resolved_steps_replay.py similarity index 100% rename from apps/api/src/cora/operation/_resolved_steps_replay.py rename to apps/api/src/cora/operation/_recipe_expansion/_resolved_steps_replay.py diff --git a/apps/api/src/cora/operation/adapters/in_memory_recipe_expander.py b/apps/api/src/cora/operation/adapters/in_memory_recipe_expander.py index 83e64468bc2..1282fd0e31e 100644 --- a/apps/api/src/cora/operation/adapters/in_memory_recipe_expander.py +++ b/apps/api/src/cora/operation/adapters/in_memory_recipe_expander.py @@ -27,7 +27,7 @@ from dataclasses import dataclass from typing import TYPE_CHECKING, Any -from cora.operation._pseudoaxis_expander import ( +from cora.operation._pseudoaxis import ( ConstituentResolver, expand_pseudoaxis_steps, ) @@ -52,7 +52,7 @@ class InMemoryRecipeExpander: need to assert provenance carries the expander identity. `constituent_resolver` defaults to None (the wiring-deferred resolver - in `_pseudoaxis_expander`, which raises for any PseudoAxis step). The + in `_pseudoaxis/_expander`, which raises for any PseudoAxis step). The Plan.wiring-backed resolver is supplied per-call by the conduct_procedure handler (loaded from Run.plan_id -> Plan.wires) via the `expand_pseudoaxis` `constituent_resolver` kwarg, which takes diff --git a/apps/api/src/cora/operation/features/reconduct_procedure/handler.py b/apps/api/src/cora/operation/features/reconduct_procedure/handler.py index 2f6c415dedb..bfcdd5fa37b 100644 --- a/apps/api/src/cora/operation/features/reconduct_procedure/handler.py +++ b/apps/api/src/cora/operation/features/reconduct_procedure/handler.py @@ -55,7 +55,7 @@ from cora.infrastructure.logging import get_logger from cora.infrastructure.ports import Deny from cora.infrastructure.routing import NIL_SENTINEL_ID -from cora.operation._resolved_steps_replay import find_resolved_steps_record +from cora.operation._recipe_expansion import find_resolved_steps_record from cora.operation.aggregates.procedure import ( InvalidProcedureReEstablishmentBoundaryError, ProcedureCannotResumeError, diff --git a/apps/api/src/cora/operation/ports/recipe_expander.py b/apps/api/src/cora/operation/ports/recipe_expander.py index a0d2f3313a3..19db45a77cc 100644 --- a/apps/api/src/cora/operation/ports/recipe_expander.py +++ b/apps/api/src/cora/operation/ports/recipe_expander.py @@ -51,7 +51,7 @@ from uuid import UUID from cora.infrastructure.ports import EventStore - from cora.operation._pseudoaxis_expander import ConstituentResolver + from cora.operation._pseudoaxis import ConstituentResolver from cora.operation.conductor import Step from cora.recipe.aggregates.recipe import RecipeStep diff --git a/apps/api/tests/architecture/test_recipe_step_variants_match_step_union.py b/apps/api/tests/architecture/test_recipe_step_variants_match_step_union.py index a093705bd29..49f7f5e3100 100644 --- a/apps/api/tests/architecture/test_recipe_step_variants_match_step_union.py +++ b/apps/api/tests/architecture/test_recipe_step_variants_match_step_union.py @@ -80,13 +80,13 @@ def test_recipe_expansion_dispatches_every_recipe_step_arm() -> None: structural shape). """ expansion_module = pytest.importorskip( - "cora.operation._recipe_expansion", - reason="_recipe_expansion module not present yet; dispatch-coverage check pending", + "cora.operation._recipe_expansion._expand", + reason="recipe-expansion module not present yet; dispatch-coverage check pending", ) recipe_arms = get_args(_recipe_body.RecipeStep) expander = getattr(expansion_module, "_expand_step", None) assert expander is not None, ( - "cora.operation._recipe_expansion is missing the _expand_step dispatch helper; " + "cora.operation._recipe_expansion._expand is missing the _expand_step dispatch helper; " "the expansion module must export it so this fitness can verify dispatch coverage." ) source = inspect.getsource(expander) diff --git a/apps/api/tests/unit/operation/test_pseudoaxis_evaluator.py b/apps/api/tests/unit/operation/test_pseudoaxis_evaluator.py index c22167c1044..8b31e18b651 100644 --- a/apps/api/tests/unit/operation/test_pseudoaxis_evaluator.py +++ b/apps/api/tests/unit/operation/test_pseudoaxis_evaluator.py @@ -39,7 +39,7 @@ ) from cora.infrastructure.adapters.in_memory_event_store import InMemoryEventStore from cora.infrastructure.kernel import Kernel -from cora.operation._pseudoaxis_evaluator import ( +from cora.operation._pseudoaxis import ( ResolvedSetpoints, resolve_pseudoaxis_command, ) diff --git a/apps/api/tests/unit/operation/test_pseudoaxis_expander.py b/apps/api/tests/unit/operation/test_pseudoaxis_expander.py index 9b08c257e29..b8445324e4c 100644 --- a/apps/api/tests/unit/operation/test_pseudoaxis_expander.py +++ b/apps/api/tests/unit/operation/test_pseudoaxis_expander.py @@ -28,7 +28,7 @@ ) from cora.infrastructure.adapters.in_memory_event_store import InMemoryEventStore from cora.infrastructure.event_envelope import to_new_event -from cora.operation._pseudoaxis_expander import expand_pseudoaxis_steps +from cora.operation._pseudoaxis import expand_pseudoaxis_steps from cora.operation.conductor import ( ActionStep, CheckStep, diff --git a/apps/api/tests/unit/operation/test_recipe_replay.py b/apps/api/tests/unit/operation/test_recipe_replay.py index 9ada6934020..2992107f8ff 100644 --- a/apps/api/tests/unit/operation/test_recipe_replay.py +++ b/apps/api/tests/unit/operation/test_recipe_replay.py @@ -1,4 +1,4 @@ -"""Unit tests for the `_recipe_replay` helpers. +"""Unit tests for the `_recipe_expansion` replay helpers. Per [[project-run-procedure-replay-design]] §Operation BC seam additions. The helpers locate the genesis `RecipeExpansionRecorded` @@ -15,11 +15,11 @@ import pytest from cora.infrastructure.ports.event_store import StoredEvent -from cora.operation._recipe_expansion import steps_to_wire -from cora.operation._recipe_replay import ( +from cora.operation._recipe_expansion import ( RecipeExpansionPins, find_recipe_expansion_record, pins_from_payload, + steps_to_wire, verify_bindings_hash, verify_steps_hash, )