fix(acms): wire ContextAssemblyPipeline as default in ACMSExecutePhaseContextAssembler

ACMSExecutePhaseContextAssembler previously instantiated the plain
ACMSPipeline when no pipeline was explicitly provided, missing production
Phase 1 optimizations including confidence-weighted strategy selection,
proportional budget allocation with min-budget enforcement, parallel
strategy execution with circuit breaking, and per-stage timing instrumentation.

The default is now ContextAssemblyPipeline which provides all of these
capabilities while remaining a drop-in replacement for ACMSPipeline.

ISSUES CLOSED: #10027
This commit is contained in:
2026-05-09 12:46:59 +00:00
committed by Forgejo
parent 6ec56ce897
commit 9da9c1b1c6
4 changed files with 74 additions and 1 deletions
+1
View File
@@ -109,3 +109,4 @@ Below are some specific details of individual PR contributions.
* HAL 9000 has contributed the CleanupService sandbox cache invalidation fix (PR #8257 / issue #7527): `_purge_sandboxes()` now invalidates the internal `_sandbox_dirs_cache` after deleting stale directories so that a subsequent `scan()` call on the same instance re-reads the filesystem instead of returning already-deleted paths as stale items.
* HAL 9000 has contributed the LSP subprocess cleanup fix (#10597): added a defensive ``_process = None`` reset before ``subprocess.Popen()`` in ``StdioTransport.start()`` to prevent orphaned child processes and file descriptor leaks when ``subprocess.Popen()`` fails during initialization.
* HAL 9000 has contributed the Invariant Data Model and Database Schema (PR #8701 / issue #8524): SQLAlchemy ORM model with fields id (UUID), description (text), created_at (timestamp), and is_active (bool); Alembic migration creating the invariants table with index on is_active for efficient active-query filtering; BDD Behave unit tests and Robot Framework integration tests.
* HAL 9000 has contributed the ACMS execute phase ContextAssemblyPipeline wiring (PR #10027): replaces the base ``ACMSPipeline`` default with ``ContextAssemblyPipeline`` in ``ACMSExecutePhaseContextAssembler``, enabling production Phase 1 components (confidence-weighted strategy selection, proportional budget allocation, parallel execution with circuit breaking) and per-stage timing instrumentation by default. Includes Behave test coverage verifying the default pipeline type.
@@ -275,3 +275,13 @@ Feature: Execute-phase context assembler coverage
And epcov scoped fragments that pass all filters
When epcov I call assemble on the plan
Then epcov the pipeline received budget max_tokens of 4096
# ---- Default pipeline type ----
Scenario: epcov default pipeline is ContextAssemblyPipeline when none provided
Given epcov an assembler with no explicit pipeline argument
Then epcov the internal pipeline should be a ContextAssemblyPipeline
Scenario: epcov explicit pipeline overrides default ContextAssemblyPipeline
Given epcov an assembler with an explicitly passed ACMSPipeline mock
Then epcov the internal pipeline should be the explicitly passed mock
@@ -124,6 +124,65 @@ def _make_assembler(
return assembler
@given("epcov an assembler with no explicit pipeline argument")
def step_epcov_no_explicit_pipeline(context: Context) -> None:
tier_service = MagicMock()
tier_service.get_scoped_view.return_value = []
repo = MagicMock()
repo.get_context_policy.return_value = ProjectContextPolicy()
context.epcov_assembler = ACMSExecutePhaseContextAssembler(
context_tier_service=tier_service,
project_repository=repo,
hot_max_tokens=4096,
)
@given("epcov an assembler with an explicitly passed ACMSPipeline mock")
def step_epcov_explicit_pipeline(context: Context) -> None:
tier_service = MagicMock()
tier_service.get_scoped_view.return_value = []
repo = MagicMock()
repo.get_context_policy.return_value = ProjectContextPolicy()
mock_pipeline = MagicMock()
context.epcov_explicit_pipeline_mock = mock_pipeline
context.epcov_assembler = ACMSExecutePhaseContextAssembler(
context_tier_service=tier_service,
project_repository=repo,
acms_pipeline=mock_pipeline,
hot_max_tokens=4096,
)
@then("epcov the internal pipeline should be a ContextAssemblyPipeline")
def step_epcov_default_pipeline_is_context_assembly(context: Context) -> None:
from cleveragents.application.services.acms_pipeline import (
ContextAssemblyPipeline,
)
assert isinstance(
context.epcov_assembler._pipeline,
ContextAssemblyPipeline,
), (
f"Expected ContextAssemblyPipeline but got "
f"{type(context.epcov_assembler._pipeline).__name__}"
)
@then("epcov the internal pipeline should be the explicitly passed mock")
def step_epcov_explicit_pipeline_kept(context: Context) -> None:
assert context.epcov_assembler._pipeline is context.epcov_explicit_pipeline_mock, (
"Expected the explicitly passed mock pipeline to be used"
)
# ---------------------------------------------------------------------------
# Protocol (line 36)
# ---------------------------------------------------------------------------
@@ -9,6 +9,9 @@ from typing import Any, Protocol
import structlog
from cleveragents.application.services.acms_pipeline import (
ContextAssemblyPipeline,
)
from cleveragents.application.services.acms_service import ACMSPipeline
from cleveragents.domain.models.acms.crp import AssembledContext, ContextRequest
from cleveragents.domain.models.acms.tiers import TieredFragment
@@ -49,7 +52,7 @@ class ACMSExecutePhaseContextAssembler(ExecutePhaseContextAssembler):
) -> None:
self._tier = context_tier_service
self._project_repository = project_repository
self._pipeline = acms_pipeline or ACMSPipeline()
self._pipeline = acms_pipeline or ContextAssemblyPipeline()
self._hot_max_tokens = hot_max_tokens
self._logger = logger.bind(component="execute_phase_context_assembler")