fix(acms): normalize context path matching for absolute paths in _path_matches #11026
@@ -46,7 +46,9 @@ Feature: Execute-phase context assembler coverage
|
||||
When epcov I check path matching for "src/foo.py" with exclude "src/secret*"
|
||||
Then epcov the path should match
|
||||
|
||||
@tdd_issue @tdd_issue_10972
|
||||
# ---- Absolute path matching with relative globs (issue #10972) ----
|
||||
|
||||
@tdd_issue @tdd_issue_10972
|
||||
Scenario: epcov path matches absolute path against relative include glob
|
||||
When epcov I check path matching for "/app/.opencode/skills/SKILL.md" with include ".opencode/**"
|
||||
Then epcov the path should match
|
||||
@@ -71,6 +73,26 @@ Feature: Execute-phase context assembler coverage
|
||||
When epcov I check path matching for "build/debug/output.log" with exclude "build/**"
|
||||
Then epcov the path should not match
|
||||
|
||||
@tdd_issue @tdd_issue_10972
|
||||
Scenario: epcov absolute path matched against relative exclude glob
|
||||
When epcov I check path matching for "/app/.opencode/skills/secret.md" with exclude ".opencode/**"
|
||||
Then epcov the path should not match
|
||||
|
||||
@tdd_issue @tdd_issue_10972
|
||||
Scenario: epcov absolute path deep subdirectory matches relative glob
|
||||
When epcov I check path matching for "/app/docs/api/endpoint.md" with include "docs/**"
|
||||
Then epcov the path should match
|
||||
|
||||
@tdd_issue @tdd_issue_10972
|
||||
Scenario: epcov absolute path excluded by nested relative glob
|
||||
When epcov I check path matching for "/app/vendor/lib/external.py" with exclude "vendor/**"
|
||||
Then epcov the path should not match
|
||||
|
||||
@tdd_issue @tdd_issue_10972
|
||||
Scenario: epcov absolute pattern (anchored with /) passed through unchanged
|
||||
When epcov I check path matching for "/src/main.py" with include "/src/**"
|
||||
Then epcov the path should match
|
||||
|
||||
# ---- _resource_matches static method ----
|
||||
|
||||
Scenario: epcov resource matches with no rules passes all
|
||||
|
||||
@@ -30,9 +30,18 @@ Feature: Project context phase analysis summaries
|
||||
Then execute phase should have fewer tokens than strategize phase
|
||||
And apply phase should have fewer or equal tokens than execute phase
|
||||
|
||||
# ---- Absolute path matching regression tests (issue #10972) ----
|
||||
|
||||
@tdd_issue @tdd_issue_10972
|
||||
Scenario: Absolute path fragments are correctly excluded by relative exclude globs
|
||||
Given a phase analysis policy with opencode exclude paths
|
||||
And an absolute path fragment for phase analysis
|
||||
When I compute project context phase analysis with budget 2000
|
||||
Then strategize phase should exclude the absolute path fragment
|
||||
|
||||
@tdd_issue @tdd_issue_10972
|
||||
Scenario: absolute path excluded by relative exclude glob in phase analysis
|
||||
Given a phase analysis policy with narrow include_paths "src/**" and strict exclude_paths ".opencode/**"
|
||||
And an absolute-path fragment for phase analysis at "/app/.opencode/skills/SKILL.md"
|
||||
When I compute project context phase analysis with budget 2000
|
||||
Then the fragment should be excluded because its path does not match include paths
|
||||
|
||||
@@ -208,7 +208,7 @@ def step_policy_opencode_exclude(context: Any) -> None:
|
||||
|
||||
|
||||
@given("an absolute path fragment for phase analysis")
|
||||
def step_absolute_path_fragment(context: Any) -> None:
|
||||
def step_absolute_path_fragment_opencode(context: Any) -> None:
|
||||
"""Fragment with an absolute path that should be excluded by relative globs."""
|
||||
context.phase_fragments = [
|
||||
TieredFragment(
|
||||
@@ -226,6 +226,35 @@ def step_absolute_path_fragment(context: Any) -> None:
|
||||
]
|
||||
|
||||
|
||||
@given(
|
||||
'a phase analysis policy with narrow include_paths "{include}" and strict exclude_paths "{exclude}"'
|
||||
)
|
||||
def step_strict_path_policy(context: Any, include: str, exclude: str) -> None:
|
||||
context.phase_policy = ProjectContextPolicy(
|
||||
default_view=ContextView(include_resources=["local/*"]),
|
||||
strategize_view=ContextView(
|
||||
include_paths=[include],
|
||||
exclude_paths=[exclude],
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@given('an absolute-path fragment for phase analysis at "{path}"')
|
||||
def step_absolute_path_fragment_at(context: Any, path: str) -> None:
|
||||
"""Create a fragment with an absolute path for testing issue #10972."""
|
||||
context.phase_fragments = [
|
||||
TieredFragment(
|
||||
fragment_id="abs-paths",
|
||||
content="x" * 50,
|
||||
tier=ContextTier.HOT,
|
||||
resource_id="local/repo-a",
|
||||
project_name="local/ctx-app",
|
||||
token_count=60,
|
||||
metadata={"path": path, "byte_size": 50_000},
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
@then("strategize phase should exclude the absolute path fragment")
|
||||
def step_strat_excludes_absolute(context: Any) -> None:
|
||||
"""Verify that the absolute path fragment is excluded by relative glob patterns."""
|
||||
@@ -234,3 +263,11 @@ def step_strat_excludes_absolute(context: Any) -> None:
|
||||
f"Expected 0 fragments (absolute path should be excluded by relative glob), "
|
||||
f"got {strat['fragment_count']}"
|
||||
)
|
||||
|
||||
|
||||
@then("the fragment should be excluded because its path does not match include paths")
|
||||
def step_fragment_excluded(context: Any) -> None:
|
||||
strat = context.phase_result["phases"]["strategize"]
|
||||
assert strat["fragment_count"] == 0, (
|
||||
f"Expected 0 fragments in strategize phase but had {strat['fragment_count']}"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user