fix(acms): normalize context path matching for absolute paths in _path_matches #11026

Merged
HAL9000 merged 6 commits from bugfix/m6-acms-path-matching-absolute into master 2026-06-13 19:27:31 +00:00
3 changed files with 70 additions and 2 deletions
@@ -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
1
@@ -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']}"
)