diff --git a/loopx/capabilities/issue_fix/__init__.py b/loopx/capabilities/issue_fix/__init__.py index ccba046702..98a61b38e3 100644 --- a/loopx/capabilities/issue_fix/__init__.py +++ b/loopx/capabilities/issue_fix/__init__.py @@ -1,14 +1,32 @@ """Public issue-fix capability boundaries.""" +from __future__ import annotations -from .pr_description import build_issue_fix_pr_description -from .periodic_report import ( - build_issue_fix_periodic_report_source, - issue_fix_periodic_report_source_adapter, -) -from .reward_memory import ( - run_issue_fix_patch_planning_reward_memory, - run_issue_fix_reviewer_artifact_reward_memory, -) +from importlib import import_module +from typing import Any + +# PR wait observation imports one owner submodule; it does not select report +# sources, PR descriptions or reward-memory workflows. Keep compatibility +# exports identical to their owners without importing them eagerly. +_EXPORTS = { + "build_issue_fix_pr_description": "pr_description", + "build_issue_fix_periodic_report_source": "periodic_report", + "issue_fix_periodic_report_source_adapter": "periodic_report", + "run_issue_fix_patch_planning_reward_memory": "reward_memory", + "run_issue_fix_reviewer_artifact_reward_memory": "reward_memory", +} + + +def __getattr__(name: str) -> Any: + module = _EXPORTS.get(name) + if module is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + value = getattr(import_module(f".{module}", __name__), name) + globals()[name] = value + return value + + +def __dir__() -> list[str]: + return sorted(set(globals()) | set(__all__)) __all__ = [ "build_issue_fix_periodic_report_source", diff --git a/loopx/capabilities/reward_memory/__init__.py b/loopx/capabilities/reward_memory/__init__.py index 7672848852..d39a22ce76 100644 --- a/loopx/capabilities/reward_memory/__init__.py +++ b/loopx/capabilities/reward_memory/__init__.py @@ -1,84 +1,80 @@ """Provider-neutral reward-memory architecture contracts.""" +from __future__ import annotations -from .architecture import ( - build_reward_memory_architecture_packet, - build_reward_memory_route_packet, - pr_3237_regression_observation, -) -from .application import ( - RewardMemoryFilteredRecallItem, - RewardMemoryRecallItem, - RewardMemoryRecallSession, - apply_reward_memory_recall, - build_active_reward_memory_record, - build_reward_memory_recall_request, - execute_reward_memory_recall, - normalize_reward_memory_provider_binding, -) -from .candidate_review import ( - build_issue_fix_reward_memory_candidate, - build_reward_memory_candidate, - issue_fix_verified_contributor_candidate_fixture, - review_reward_memory_candidate, -) -from .codex_app_outcome import ( - run_staged_codex_app_turn_outcome_ingest, - run_staged_codex_app_turn_outcome_ingest_fail_open, - stage_codex_app_turn_outcome_candidate, - stage_codex_app_turn_outcome_candidate_fail_open, -) -from .health import ( - build_reward_memory_corpus_health_packet, - reward_memory_health_case, -) -from .ingestion import ( - ingest_reward_memory_candidate, - normalize_reward_memory_standing_policy, -) -from .evaluation import run_reward_memory_evaluation -from .decision import ( - RewardMemoryDecisionResult, - assess_reward_memory_decision, - run_reward_memory_decision, -) -from .dogfood import ( - build_reward_memory_dogfood_batch, - build_reward_memory_dogfood_receipt, - build_reward_memory_operator_control, -) -from .memory_utility import ( - MEMORY_UTILITY_OBSERVATION_SCHEMA_VERSION, - build_reward_memory_utility_observation, - reward_memory_application_receipt_id, - validate_reward_memory_utility_observation, -) -from .utility_reducer import ( - MEMORY_UTILITY_PROJECTION_SCHEMA_VERSION, - MEMORY_UTILITY_REDUCER_VERSION, - UTILITY_MAX, - UTILITY_MIN, - build_reward_memory_utility_projection, - reduce_reward_memory_utility, - reduce_reward_memory_utility_observations, - reward_memory_utility_reducer_identity, - validate_reward_memory_utility_projection, -) -from .registry import ( - build_reward_memory_corpus_registry_packet, - normalize_reward_memory_corpus, - semantic_preference_inventory_to_reward_corpora, -) -from .runtime_hooks import ( - run_reward_memory_automatic_ingest_hook, - run_reward_memory_automatic_recall_hook, -) -from .read_authority import build_reward_memory_surface_read_authority_checkpoints -from .outcome_lifecycle import ( - reconcile_pending_turn_outcome_ingests, - reconcile_pending_turn_outcome_ingests_fail_open, - run_configured_turn_outcome_ingest, - run_configured_turn_outcome_ingest_fail_open, -) +from importlib import import_module +from typing import Any + +# Turn hooks import one owner submodule; they do not select evaluation, dogfood +# or Codex App staging. Keep compatibility exports identical to their owners +# without importing every reward-memory workflow when a submodule loads. +_EXPORTS = { + "build_reward_memory_architecture_packet": "architecture", + "build_reward_memory_route_packet": "architecture", + "pr_3237_regression_observation": "architecture", + "RewardMemoryFilteredRecallItem": "application", + "RewardMemoryRecallItem": "application", + "RewardMemoryRecallSession": "application", + "apply_reward_memory_recall": "application", + "build_active_reward_memory_record": "application", + "build_reward_memory_recall_request": "application", + "execute_reward_memory_recall": "application", + "normalize_reward_memory_provider_binding": "application", + "build_issue_fix_reward_memory_candidate": "candidate_review", + "build_reward_memory_candidate": "candidate_review", + "issue_fix_verified_contributor_candidate_fixture": "candidate_review", + "review_reward_memory_candidate": "candidate_review", + "run_staged_codex_app_turn_outcome_ingest": "codex_app_outcome", + "run_staged_codex_app_turn_outcome_ingest_fail_open": "codex_app_outcome", + "stage_codex_app_turn_outcome_candidate": "codex_app_outcome", + "stage_codex_app_turn_outcome_candidate_fail_open": "codex_app_outcome", + "build_reward_memory_corpus_health_packet": "health", + "reward_memory_health_case": "health", + "ingest_reward_memory_candidate": "ingestion", + "normalize_reward_memory_standing_policy": "ingestion", + "run_reward_memory_evaluation": "evaluation", + "RewardMemoryDecisionResult": "decision", + "assess_reward_memory_decision": "decision", + "run_reward_memory_decision": "decision", + "build_reward_memory_dogfood_batch": "dogfood", + "build_reward_memory_dogfood_receipt": "dogfood", + "build_reward_memory_operator_control": "dogfood", + "MEMORY_UTILITY_OBSERVATION_SCHEMA_VERSION": "memory_utility", + "build_reward_memory_utility_observation": "memory_utility", + "reward_memory_application_receipt_id": "memory_utility", + "validate_reward_memory_utility_observation": "memory_utility", + "MEMORY_UTILITY_PROJECTION_SCHEMA_VERSION": "utility_reducer", + "MEMORY_UTILITY_REDUCER_VERSION": "utility_reducer", + "UTILITY_MAX": "utility_reducer", + "UTILITY_MIN": "utility_reducer", + "build_reward_memory_utility_projection": "utility_reducer", + "reduce_reward_memory_utility": "utility_reducer", + "reduce_reward_memory_utility_observations": "utility_reducer", + "reward_memory_utility_reducer_identity": "utility_reducer", + "validate_reward_memory_utility_projection": "utility_reducer", + "build_reward_memory_corpus_registry_packet": "registry", + "normalize_reward_memory_corpus": "registry", + "semantic_preference_inventory_to_reward_corpora": "registry", + "run_reward_memory_automatic_ingest_hook": "runtime_hooks", + "run_reward_memory_automatic_recall_hook": "runtime_hooks", + "build_reward_memory_surface_read_authority_checkpoints": "read_authority", + "reconcile_pending_turn_outcome_ingests": "outcome_lifecycle", + "reconcile_pending_turn_outcome_ingests_fail_open": "outcome_lifecycle", + "run_configured_turn_outcome_ingest": "outcome_lifecycle", + "run_configured_turn_outcome_ingest_fail_open": "outcome_lifecycle", +} + + +def __getattr__(name: str) -> Any: + module = _EXPORTS.get(name) + if module is None: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + value = getattr(import_module(f".{module}", __name__), name) + globals()[name] = value + return value + + +def __dir__() -> list[str]: + return sorted(set(globals()) | set(__all__)) __all__ = [ "build_reward_memory_architecture_packet", diff --git a/tests/test_cli_entrypoint.py b/tests/test_cli_entrypoint.py index a011c354a8..8c135a3480 100644 --- a/tests/test_cli_entrypoint.py +++ b/tests/test_cli_entrypoint.py @@ -390,6 +390,37 @@ def test_turn_facade_preserves_every_export_owner_identity() -> None: assert completed.returncode == 0, completed.stderr +def test_capability_hook_imports_defer_unselected_facade_exports() -> None: + script = """ +from importlib import import_module +import sys + +import loopx.capabilities.issue_fix.pr_wait_hook +import loopx.capabilities.reward_memory.turn_hooks + +for name in ( + "issue_fix.periodic_report", + "issue_fix.pr_description", + "issue_fix.reward_memory", + "reward_memory.codex_app_outcome", + "reward_memory.dogfood", + "reward_memory.evaluation", +): + assert "loopx.capabilities." + name not in sys.modules, name + +from loopx.capabilities import issue_fix, reward_memory +for facade in (issue_fix, reward_memory): + assert set(facade._EXPORTS) == set(facade.__all__) + for name in facade.__all__: + value = getattr(facade, name) + owner = import_module("." + facade._EXPORTS[name], facade.__name__) + assert value is getattr(owner, name), name + assert name in dir(facade) +""" + completed = run_isolated_script(script) + assert completed.returncode == 0, completed.stderr + + def test_quota_facade_lazy_exports_preserve_owner_identity() -> None: script = """ from importlib import import_module