"""Behave steps for project_context_phase_analysis.feature.""" from __future__ import annotations from typing import Any from behave import given, then, when from cleveragents.application.services.context_phase_analysis import ( analyze_phase_summaries, ) from cleveragents.domain.models.acms.tiers import ContextTier, TieredFragment from cleveragents.domain.models.core.context_policy import ( ContextView, ProjectContextPolicy, ) def _base_policy() -> ProjectContextPolicy: return ProjectContextPolicy( default_view=ContextView( include_resources=["local/*"], max_file_size=2_000_000, max_total_size=5_000_000, ), strategize_view=ContextView( include_resources=["local/*"], include_paths=["src/**/*.py", "docs/**/*.md"], exclude_paths=["**/test_*.py"], max_file_size=1_000_000, max_total_size=2_000_000, ), execute_view=ContextView( include_resources=["local/*"], include_paths=["src/**/*.py"], exclude_paths=["**/test_*.py", "**/conftest.py"], max_file_size=500_000, max_total_size=700_000, ), apply_view=ContextView( include_resources=["local/*"], include_paths=["src/**/*.py"], exclude_paths=["docs/**"], max_file_size=250_000, max_total_size=400_000, ), ) @given("a phase analysis policy with narrowing limits") def step_policy(context: Any) -> None: context.phase_policy = _base_policy() @given("no tiered fragments for phase analysis") def step_no_fragments(context: Any) -> None: context.phase_fragments = [] @given("a single resource fragment for phase analysis") def step_single_fragment(context: Any) -> None: context.phase_fragments = [ TieredFragment( fragment_id="single", content="x" * 100, tier=ContextTier.HOT, resource_id="local/repo-a", project_name="local/ctx-app", token_count=120, metadata={"path": "src/main.py", "byte_size": 100_000}, ) ] @given("multiple resource fragments for phase analysis") def step_multiple_resources(context: Any) -> None: context.phase_fragments = [ TieredFragment( fragment_id="a", content="x" * 80, tier=ContextTier.HOT, resource_id="local/repo-a", project_name="local/ctx-app", token_count=100, metadata={"path": "src/a.py", "byte_size": 80_000}, ), TieredFragment( fragment_id="b", content="x" * 90, tier=ContextTier.WARM, resource_id="local/repo-b", project_name="local/ctx-app", token_count=110, metadata={"path": "src/b.py", "byte_size": 90_000}, ), ] @given("budget constrained fragments for phase analysis") def step_budget_fragments(context: Any) -> None: context.phase_fragments = [ TieredFragment( fragment_id="f1", content="x" * 400, tier=ContextTier.HOT, resource_id="local/repo-a", project_name="local/ctx-app", token_count=450, metadata={"path": "src/core.py", "byte_size": 400_000}, ), TieredFragment( fragment_id="f2", content="x" * 380, tier=ContextTier.WARM, resource_id="local/repo-a", project_name="local/ctx-app", token_count=430, metadata={"path": "src/engine.py", "byte_size": 380_000}, ), TieredFragment( fragment_id="f3", content="x" * 200, tier=ContextTier.COLD, resource_id="local/repo-a", project_name="local/ctx-app", token_count=220, metadata={"path": "docs/design.md", "byte_size": 200_000}, ), ] @when("I compute project context phase analysis with budget {budget:d}") def step_compute(context: Any, budget: int) -> None: context.phase_result = analyze_phase_summaries( context.phase_policy, context.phase_fragments, budget, ) @then("each phase summary should include resource and size metrics") def step_metrics_present(context: Any) -> None: for phase in ("strategize", "execute", "apply"): row = context.phase_result["phases"][phase] assert "resource_count" in row assert "total_bytes" in row assert "budget_used_ratio" in row @then("phase analysis should report monotonic narrowing") def step_narrowing(context: Any) -> None: narrowing = context.phase_result["narrowing"] assert narrowing["overall"] is True @then("each phase summary should include human-readable text") def step_summary_text(context: Any) -> None: for phase in ("strategize", "execute", "apply"): summary = context.phase_result["phases"][phase]["summary"] assert isinstance(summary, str) assert phase in summary @then("strategize phase should include at least one fragment") def step_strat_non_empty(context: Any) -> None: assert context.phase_result["phases"]["strategize"]["fragment_count"] >= 1 @then("strategize phase resource count should be 2") def step_resource_count_two(context: Any) -> None: assert context.phase_result["phases"]["strategize"]["resource_count"] == 2 @then("execute phase should have fewer tokens than strategize phase") def step_execute_less(context: Any) -> None: strat_tokens = context.phase_result["phases"]["strategize"]["total_tokens"] exec_tokens = context.phase_result["phases"]["execute"]["total_tokens"] assert exec_tokens < strat_tokens @then("apply phase should have fewer or equal tokens than execute phase") def step_apply_less_or_equal(context: Any) -> None: exec_tokens = context.phase_result["phases"]["execute"]["total_tokens"] apply_tokens = context.phase_result["phases"]["apply"]["total_tokens"] assert apply_tokens <= exec_tokens