Files
temp/features/steps/acms_advanced_strategies_coverage_boost_steps.py
freemo a808c395f9 test(coverage): add Behave BDD tests to improve unit test coverage across 53 source modules
Add 53 new .feature files and corresponding step definition files targeting
uncovered lines identified in build/coverage.xml. Fix AmbiguousStep conflicts
in 7 pre-existing step files by disambiguating step text.

New tests cover: ACP clients/facade, actor CLI/config, application container,
ACMS service/strategies, async worker, automation profile CLI, autonomy
guardrail, bridge, change model, config CLI/service, context service,
cross-plan correction, database models, decision service, decomposition
clustering/service, discovery handler, langchain chat provider, langgraph
nodes, materializers, multi-project service, plan apply/CLI/lifecycle/model/
preflight/resume/service, PostgreSQL analyzer, project CLI/context CLI,
provider registry, reactive application/route, repositories, resolver handler,
resource registry service, resume model, retry patterns, sandbox protocol,
server CLI, skill CLI/service, skills registry, subplan execution/service,
system CLI, UKO loader, UoW, and YAML template engine.

Closes #645
2026-03-09 13:01:58 -04:00

306 lines
10 KiB
Python

"""Step definitions for acms_advanced_strategies_coverage_boost.feature.
Targets uncovered lines in acms_advanced_strategies.py:
- Line 97 : ArceStrategy.max_iterations property
- Line 158 : ARCE refinement loop non-convergence path (prev_total update)
- Line 208 : _refine_scores empty-fragments early return
- Lines 230-231: _refine_scores prefix-boost branch
- Lines 267-269: TemporalArchaeologyStrategy.capabilities
- Lines 364-365: PlanDecisionContextStrategy.capabilities
"""
from __future__ import annotations
from typing import Any
from behave import given, then, when
from behave.runner import Context
from cleveragents.application.services.acms_advanced_strategies import (
ArceStrategy,
PlanDecisionContextStrategy,
TemporalArchaeologyStrategy,
)
from cleveragents.domain.models.core.context_fragment import (
ContextBudget,
ContextFragment,
FragmentProvenance,
)
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
_DEFAULT_PROVENANCE = FragmentProvenance(resource_uri="test://boost")
def _frag(
content: str = "hello world",
*,
fragment_id: str | None = None,
relevance_score: float = 0.5,
detail_depth: int = 3,
token_count: int = 10,
tier: str = "warm",
uko_node: str = "test://default/node",
) -> ContextFragment:
"""Create a ContextFragment with test defaults."""
kwargs: dict[str, Any] = {
"content": content,
"relevance_score": relevance_score,
"detail_depth": detail_depth,
"token_count": token_count,
"tier": tier,
"uko_node": uko_node,
"provenance": _DEFAULT_PROVENANCE,
}
if fragment_id is not None:
kwargs["fragment_id"] = fragment_id
return ContextFragment(**kwargs)
def _large_budget() -> ContextBudget:
"""Return a budget large enough that packing never truncates."""
return ContextBudget(max_tokens=100_000, reserved_tokens=0)
# ===================================================================
# ArceStrategy — max_iterations property (line 97)
# ===================================================================
@given("an ArceStrategy with max_iterations set to {n:d}")
def step_arce_with_max_iter(context: Context, n: int) -> None:
context.strategy = ArceStrategy(max_iterations=n)
@given("an ArceStrategy with default parameters")
def step_arce_default(context: Context) -> None:
context.strategy = ArceStrategy()
@then("the max_iterations property returns {n:d}")
def step_max_iterations_value(context: Context, n: int) -> None:
assert context.strategy.max_iterations == n, (
f"Expected max_iterations={n}, got {context.strategy.max_iterations}"
)
# ===================================================================
# ARCE refinement loop — non-convergence path (line 158)
# ===================================================================
@given(
"an ArceStrategy with max_iterations set to {n:d} "
"and convergence_threshold {thresh:g}"
)
def step_arce_custom(context: Context, n: int, thresh: float) -> None:
context.strategy = ArceStrategy(
max_iterations=n,
convergence_threshold=thresh,
)
@given(
"a set of fragments with shared UKO prefixes "
"that produce score changes each iteration"
)
def step_shared_prefix_fragments(context: Context) -> None:
# We need enough fragments so that the anchor set and non-anchor set
# share a UKO prefix. The non-anchor fragments will get a 0.05 boost
# each iteration, ensuring improvement > 0 (threshold is 0.0 means
# convergence is impossible, so the loop always runs to max_iterations).
#
# Fragment A — high relevance, anchor candidate
# Fragments B, C — same UKO prefix as A, lower relevance -> boosted
# Fragment D — different prefix, no boost
context.fragments = [
_frag(
content="anchor content for domain",
fragment_id="frag-A",
relevance_score=0.9,
detail_depth=5,
uko_node="uko://domain/module/a",
),
_frag(
content="related content for domain module",
fragment_id="frag-B",
relevance_score=0.3,
detail_depth=2,
uko_node="uko://domain/module/b",
),
_frag(
content="another related domain module item",
fragment_id="frag-C",
relevance_score=0.2,
detail_depth=1,
uko_node="uko://domain/module/c",
),
_frag(
content="unrelated item in other prefix",
fragment_id="frag-D",
relevance_score=0.4,
detail_depth=4,
uko_node="uko://other/section/d",
),
]
@when("the ArceStrategy assembles the fragments with a large budget")
def step_arce_assemble(context: Context) -> None:
context.result = context.strategy.assemble(
context.fragments,
_large_budget(),
)
@then("the result contains fragments ordered by refined score")
def step_result_ordered(context: Context) -> None:
result = context.result
assert len(result) == len(context.fragments), (
f"Expected {len(context.fragments)} fragments, got {len(result)}"
)
# Just verify we got a non-empty ordering — the detailed score
# verification is done in the prefix-boost scenario below.
assert len(result) > 0
# ===================================================================
# _refine_scores — empty fragments guard (line 208)
# ===================================================================
@when("_refine_scores is called with an empty fragment list and some scores")
def step_refine_empty(context: Context) -> None:
original_scores = {"phantom-id": 0.42}
context.refine_result = context.strategy._refine_scores([], original_scores)
context.original_scores = original_scores
@then("the original scores dict is returned unchanged")
def step_refine_unchanged(context: Context) -> None:
assert context.refine_result == context.original_scores
# ===================================================================
# _refine_scores — prefix-boost branch (lines 230-231)
# ===================================================================
@given("four fragments where two share UKO prefix with the anchor fragment")
def step_four_fragments(context: Context) -> None:
# With 4 fragments the anchor set is max(1, 4*3//10) = 1.
# Only the highest-scored fragment becomes the anchor.
# Fragments sharing its UKO prefix but NOT in the anchor set get boosted.
context.boost_fragments = [
_frag(
content="anchor content item",
fragment_id="anchor-1",
relevance_score=0.9,
detail_depth=5,
uko_node="uko://shared/prefix/a",
),
_frag(
content="same prefix lower score",
fragment_id="related-2",
relevance_score=0.3,
detail_depth=2,
uko_node="uko://shared/prefix/b",
),
_frag(
content="same prefix even lower",
fragment_id="related-3",
relevance_score=0.2,
detail_depth=1,
uko_node="uko://shared/prefix/c",
),
_frag(
content="different prefix item",
fragment_id="other-4",
relevance_score=0.4,
detail_depth=4,
uko_node="uko://different/prefix/d",
),
]
# Pre-compute initial scores (same algorithm as _initial_score)
context.boost_scores: dict[str, float] = {}
for frag in context.boost_fragments:
depth_norm = frag.detail_depth / 9.0
words = set(frag.content.lower().split())
diversity = min(len(words) / max(frag.token_count, 1), 1.0)
score = frag.relevance_score * 0.4 + depth_norm * 0.3 + diversity * 0.3
context.boost_scores[frag.fragment_id] = score
@when("_refine_scores is called with those fragments and initial scores")
def step_refine_with_fragments(context: Context) -> None:
context.refined_scores = context.strategy._refine_scores(
context.boost_fragments,
context.boost_scores,
)
@then("non-anchor fragments sharing the anchor prefix receive a 0.05 boost")
def step_verify_boost(context: Context) -> None:
original = context.boost_scores
refined = context.refined_scores
# anchor-1 has the highest score and is in the anchor set — no boost
assert refined["anchor-1"] == original["anchor-1"], (
"Anchor fragment should NOT be boosted"
)
# related-2 and related-3 share the anchor prefix and are NOT anchors
# so they should be boosted by 0.05
for fid in ("related-2", "related-3"):
expected = min(original[fid] + 0.05, 1.0)
assert abs(refined[fid] - expected) < 1e-9, (
f"{fid}: expected {expected}, got {refined[fid]}"
)
# other-4 has a different prefix — no boost
assert refined["other-4"] == original["other-4"], (
"Fragment with a different prefix should NOT be boosted"
)
# ===================================================================
# TemporalArchaeologyStrategy.capabilities (lines 267-269)
# ===================================================================
@given("a TemporalArchaeologyStrategy instance")
def step_temporal_instance(context: Context) -> None:
context.strategy = TemporalArchaeologyStrategy()
@then("its capabilities have supports_temporal_archaeology true")
def step_cap_temporal(context: Context) -> None:
assert context.strategy.capabilities.supports_temporal_archaeology is True
@then("its capabilities have supports_graph_navigation true")
def step_cap_graph_true(context: Context) -> None:
assert context.strategy.capabilities.supports_graph_navigation is True
@then("its capabilities have supports_semantic_search false")
def step_cap_semantic_false(context: Context) -> None:
assert context.strategy.capabilities.supports_semantic_search is False
# ===================================================================
# PlanDecisionContextStrategy.capabilities (lines 364-365)
# ===================================================================
@given("a PlanDecisionContextStrategy instance")
def step_plan_decision_instance(context: Context) -> None:
context.strategy = PlanDecisionContextStrategy()
@then("its capabilities have supports_graph_navigation false")
def step_cap_graph_false(context: Context) -> None:
assert context.strategy.capabilities.supports_graph_navigation is False