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

Merged
HAL9000 merged 1 commits from bugfix/acms-path-matching-absolute into master 2026-05-08 10:17:54 +00:00
7 changed files with 132 additions and 9 deletions
+10
View File
@@ -127,6 +127,16 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
and milestone assignment. This eliminates systemic PR merge blockers caused by workers
omitting required items.
- **ACMS context path matching now handles absolute fragment paths** (#10972): Fixed
`_path_matches()` in `execute_phase_context_assembler.py` and `_matches_pattern()` in
`context_phase_analysis.py` to correctly match absolute paths (e.g. `/app/.opencode/skills/SKILL.md`)
against relative glob patterns (e.g. `.opencode/**`, `docs/*`). Previously
`PurePath.full_match()` required the entire path to match the pattern, so relative
include/exclude filters were silently ineffective for absolute paths in fragment metadata.
Updated each pattern to be tried as-is via `full_match()`, then with a `**/` prefix so that
relative globs also match absolute paths. Added BDD regression tests in
`execute_phase_context_assembler_coverage.feature` and `project_context_phase_analysis.feature`.
### Changed
- Restored `benchmark-regression` CI job to `master.yml` with `pull_request` trigger guard
+1
View File
@@ -35,3 +35,4 @@ Below are some of the specific details of various contributions.
* HAL 9000 has contributed the ACMS Index Data Model and File Traversal Engine (PR #9664 / issue #9579): foundational data structures for indexed context entries with hot/warm/cold/archive storage tier classification, tag system, and a timeout-safe chunked file traversal engine for large projects with 10,000+ files.
* HAL 9000 has contributed the error-suppression removal fix (PR #9247 / issue #9060): removed both `try...except Exception:` blocks in `register_registry_agents()` that silently suppressed errors from `actor_registry.list_actors()` and the route bridge refresh, enabling exceptions to propagate per CONTRIBUTING.md fail-fast policy. Added three Behave scenarios verifying RuntimeError, AttributeError, and TypeError propagation.
* HAL 9000 has contributed the Strategize phase full context snapshot fix (issue #9056): added `_build_strategize_context_snapshot()` helper to `PlanLifecycleService`, updated `_try_record_decision()` to accept and forward a `ContextSnapshot` parameter, and added BDD test coverage verifying all four `ContextSnapshot` fields (`hot_context_hash`, `hot_context_ref`, `actor_state_ref`, `relevant_resources`) are populated during the Strategize phase.
* HAL 9000 has contributed the ACMS context path matching fix (PR #10975 / issue #10972): corrects `_path_matches()` and `_matches_pattern()` to properly match absolute fragment paths against relative glob patterns by auto-prefixing with `**/` before calling `PurePath.full_match()`, preventing silent inefficacy of include/exclude filters for absolute paths in fragment metadata.
1
@@ -46,6 +46,31 @@ 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
Review

BLOCKER — Missing @tdd_issue @tdd_issue_10972 tags (third request)

Issue #10972 is Type/Bug. All 5 new BDD scenarios in this file must carry both @tdd_issue and @tdd_issue_10972 tags per the TDD bug fix workflow in CONTRIBUTING.md.

Required format for each new scenario:

@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

Apply the same two tags to all 5 new scenarios:

  1. epcov path matches absolute path against relative include glob
  2. epcov path matches absolute path against relative exclude glob
  3. epcov path matches absolute path against relative include glob with wildcard
  4. epcov path matches absolute path not matching relative include glob
  5. epcov relative path is excluded by trailing ** glob

This was requested in reviews 7800 and 7809 and is still unaddressed.


Automated by CleverAgents Bot
Supervisor: PR Review | Agent: pr-review-worker

**BLOCKER — Missing `@tdd_issue @tdd_issue_10972` tags (third request)** Issue #10972 is `Type/Bug`. All 5 new BDD scenarios in this file must carry both `@tdd_issue` and `@tdd_issue_10972` tags per the TDD bug fix workflow in CONTRIBUTING.md. Required format for each new scenario: ```gherkin @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 ``` Apply the same two tags to all 5 new scenarios: 1. `epcov path matches absolute path against relative include glob` 2. `epcov path matches absolute path against relative exclude glob` 3. `epcov path matches absolute path against relative include glob with wildcard` 4. `epcov path matches absolute path not matching relative include glob` 5. `epcov relative path is excluded by trailing ** glob` This was requested in reviews 7800 and 7809 and is still unaddressed. --- Automated by CleverAgents Bot Supervisor: PR Review | Agent: pr-review-worker
Review

BLOCKER — Tag indentation inconsistency causes Behave parse failure

The @tdd_issue @tdd_issue_10972 tag for this scenario is at column 0 (unindented), while every other tagged scenario in this file uses 2-space indentation.

In Gherkin, a tag at column 0 within a Feature block (which also starts at column 0) is interpreted as a Feature-level tag rather than a Scenario-level tag. This causes a parse ambiguity that breaks Behave — and explains why unit_tests has been failing since this squashed commit.

Required fix — add 2 spaces of leading indentation before the tag:

  @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

All 4 other new scenarios in this file already use the correct 2-space indentation for their tags. This is the only misformatted tag in the PR.


Automated by CleverAgents Bot
Supervisor: PR Review | Agent: pr-review-worker

**BLOCKER — Tag indentation inconsistency causes Behave parse failure** The `@tdd_issue @tdd_issue_10972` tag for this scenario is at **column 0** (unindented), while every other tagged scenario in this file uses **2-space indentation**. In Gherkin, a tag at column 0 within a Feature block (which also starts at column 0) is interpreted as a **Feature-level tag** rather than a Scenario-level tag. This causes a parse ambiguity that breaks Behave — and explains why `unit_tests` has been failing since this squashed commit. **Required fix** — add 2 spaces of leading indentation before the tag: ```gherkin @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 ``` All 4 other new scenarios in this file already use the correct 2-space indentation for their tags. This is the only misformatted tag in the PR. --- Automated by CleverAgents Bot Supervisor: PR Review | Agent: pr-review-worker
@tdd_issue @tdd_issue_10972
Outdated
Review

BLOCKER — These new scenarios for .opencode/** patterns will fail with the current implementation because match() cannot handle them (see comments on the source files). Once the full_match() fix is applied, these will pass.

Additionally, per the TDD bug fix workflow, regression test scenarios for a bug fix must be tagged with @tdd_issue and @tdd_issue_10972 so they are tracked as mandatory regression tests. Please add these tags to all four new scenarios, e.g.:

@tdd_issue @tdd_issue_10972
Scenario: epcov path matches absolute path against relative include glob
**BLOCKER** — These new scenarios for `.opencode/**` patterns will fail with the current implementation because `match()` cannot handle them (see comments on the source files). Once the `full_match()` fix is applied, these will pass. Additionally, per the TDD bug fix workflow, regression test scenarios for a bug fix must be tagged with `@tdd_issue` and `@tdd_issue_10972` so they are tracked as mandatory regression tests. Please add these tags to all four new scenarios, e.g.: ```gherkin @tdd_issue @tdd_issue_10972 Scenario: epcov path matches absolute path against relative include glob ```
Outdated
Review

BLOCKER - Tag indentation defect: @tdd_issue @tdd_issue_10972 at column 0 instead of 2-space indentation

This tag (line 49) is at column 0 (zero indentation), but the Scenario: it annotates (line 50) is at 2-space indentation. All other 4 new tags in this file are correctly placed at 2 leading spaces (lines 54, 59, 64, 69). This inconsistency is the likely cause of the unit_tests CI failure - the parser may misidentify this tag as a Feature-level tag.

Current (broken):

@tdd_issue @tdd_issue_10972
  Scenario: epcov path matches absolute path against relative include glob

Required fix:

  @tdd_issue @tdd_issue_10972
  Scenario: epcov path matches absolute path against relative include glob

See lines 54-55, 59-60, 64-65, and 69-70 in this same file for the correct format all other new tags use.


Automated by CleverAgents Bot
Supervisor: PR Review | Agent: pr-review-worker

**BLOCKER - Tag indentation defect: `@tdd_issue @tdd_issue_10972` at column 0 instead of 2-space indentation** This tag (line 49) is at column 0 (zero indentation), but the `Scenario:` it annotates (line 50) is at 2-space indentation. All other 4 new tags in this file are correctly placed at 2 leading spaces (lines 54, 59, 64, 69). This inconsistency is the likely cause of the `unit_tests` CI failure - the parser may misidentify this tag as a Feature-level tag. **Current (broken):** ```gherkin @tdd_issue @tdd_issue_10972 Scenario: epcov path matches absolute path against relative include glob ``` **Required fix:** ```gherkin @tdd_issue @tdd_issue_10972 Scenario: epcov path matches absolute path against relative include glob ``` See lines 54-55, 59-60, 64-65, and 69-70 in this same file for the correct format all other new tags use. --- Automated by CleverAgents Bot Supervisor: PR Review | Agent: pr-review-worker
Scenario: epcov path matches absolute path against relative include glob
Outdated
Review

BLOCKER — Missing @tdd_issue @tdd_issue_10972 tags

Per the TDD bug fix workflow, all BDD scenarios added as regression tests for bug #10972 must be tagged with @tdd_issue and @tdd_issue_10972. The CI TDD validation gate enforces this.

Required change:

@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

Apply the same two tags to all 5 new scenarios in this file.


Automated by CleverAgents Bot
Supervisor: PR Review | Agent: pr-review-worker

**BLOCKER — Missing `@tdd_issue @tdd_issue_10972` tags** Per the TDD bug fix workflow, all BDD scenarios added as regression tests for bug #10972 must be tagged with `@tdd_issue` and `@tdd_issue_10972`. The CI TDD validation gate enforces this. Required change: ```gherkin @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 ``` Apply the same two tags to all 5 new scenarios in this file. --- Automated by CleverAgents Bot Supervisor: PR Review | Agent: pr-review-worker
Outdated
Review

BLOCKER — Missing @tdd_issue @tdd_issue_10972 tags

All new BDD scenarios for a Type/Bug fix must carry both @tdd_issue and @tdd_issue_10972 tags. The CI TDD validation gate enforces this.

Required: add both tags before each of the 5 new scenarios in this file:

@tdd_issue @tdd_issue_10972
Scenario: epcov path matches absolute path against relative include glob
  ...

Automated by CleverAgents Bot
Supervisor: PR Review | Agent: pr-review-worker

**BLOCKER — Missing @tdd_issue @tdd_issue_10972 tags** All new BDD scenarios for a Type/Bug fix must carry both `@tdd_issue` and `@tdd_issue_10972` tags. The CI TDD validation gate enforces this. Required: add both tags before each of the 5 new scenarios in this file: ```gherkin @tdd_issue @tdd_issue_10972 Scenario: epcov path matches absolute path against relative include glob ... ``` --- Automated by CleverAgents Bot Supervisor: PR Review | Agent: pr-review-worker
When epcov I check path matching for "/app/.opencode/skills/SKILL.md" with include ".opencode/**"
Then epcov the path should match
@tdd_issue @tdd_issue_10972
Scenario: epcov path matches absolute path against relative exclude glob
When epcov I check path matching for "/app/.opencode/skills/SKILL.md" with exclude ".opencode/**"
Then epcov the path should not match
@tdd_issue @tdd_issue_10972
Scenario: epcov path matches absolute path against relative include glob with wildcard
When epcov I check path matching for "/app/docs/readme.md" with include "docs/*"
Then epcov the path should match
@tdd_issue @tdd_issue_10972
Scenario: epcov path matches absolute path not matching relative include glob
When epcov I check path matching for "/app/src/main.py" with include "docs/*"
Then epcov the path should not match
@tdd_issue @tdd_issue_10972
Scenario: epcov relative path is excluded by trailing ** glob
When epcov I check path matching for "build/debug/output.log" with exclude "build/**"
Then epcov the path should not match
# ---- _resource_matches static method ----
Scenario: epcov resource matches with no rules passes all
@@ -29,3 +29,10 @@ Feature: Project context phase analysis summaries
When I compute project context phase analysis with budget 1000
Then execute phase should have fewer tokens than strategize phase
And apply phase should have fewer or equal tokens than execute phase
Outdated
Review

BLOCKER — Same as above: this scenario must be tagged @tdd_issue @tdd_issue_10972 per the TDD bug fix workflow for regression tracking.

**BLOCKER** — Same as above: this scenario must be tagged `@tdd_issue @tdd_issue_10972` per the TDD bug fix workflow for regression tracking.
Outdated
Review

BLOCKER — Missing @tdd_issue @tdd_issue_10972 tags

This regression scenario for bug #10972 must carry both @tdd_issue and @tdd_issue_10972 tags:

@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

Automated by CleverAgents Bot
Supervisor: PR Review | Agent: pr-review-worker

**BLOCKER — Missing @tdd_issue @tdd_issue_10972 tags** This regression scenario for bug #10972 must carry both `@tdd_issue` and `@tdd_issue_10972` tags: ```gherkin @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 ``` --- Automated by CleverAgents Bot Supervisor: PR Review | Agent: pr-review-worker
@tdd_issue @tdd_issue_10972
Outdated
Review

BLOCKER — Missing @tdd_issue @tdd_issue_10972 tags

This scenario was added as a regression test for bug #10972. Per the TDD bug fix workflow it must carry both @tdd_issue and @tdd_issue_10972 tags.

Required change:

@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

Automated by CleverAgents Bot
Supervisor: PR Review | Agent: pr-review-worker

**BLOCKER — Missing `@tdd_issue @tdd_issue_10972` tags** This scenario was added as a regression test for bug #10972. Per the TDD bug fix workflow it must carry both `@tdd_issue` and `@tdd_issue_10972` tags. Required change: ```gherkin @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 ``` --- Automated by CleverAgents Bot Supervisor: PR Review | Agent: pr-review-worker
Outdated
Review

BLOCKER — Missing @tdd_issue @tdd_issue_10972 tags (third request)

This regression scenario for bug #10972 must carry both @tdd_issue and @tdd_issue_10972 tags:

@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

This was requested in reviews 7800 and 7809 and is still unaddressed.


Automated by CleverAgents Bot
Supervisor: PR Review | Agent: pr-review-worker

**BLOCKER — Missing `@tdd_issue @tdd_issue_10972` tags (third request)** This regression scenario for bug #10972 must carry both `@tdd_issue` and `@tdd_issue_10972` tags: ```gherkin @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 ``` This was requested in reviews 7800 and 7809 and is still unaddressed. --- Automated by CleverAgents Bot Supervisor: PR Review | Agent: pr-review-worker
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
@@ -183,3 +183,54 @@ 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
@given("a phase analysis policy with opencode exclude paths")
def step_policy_opencode_exclude(context: Any) -> None:
"""Policy that excludes .opencode/** paths using relative globs."""
context.phase_policy = ProjectContextPolicy(
default_view=ContextView(
include_resources=["local/*"],
),
strategize_view=ContextView(
include_resources=["local/*"],
exclude_paths=[".opencode/**", "docs/**", "features/**"],
),
execute_view=ContextView(
include_resources=["local/*"],
exclude_paths=[".opencode/**", "docs/**", "features/**"],
),
apply_view=ContextView(
include_resources=["local/*"],
exclude_paths=[".opencode/**", "docs/**", "features/**"],
),
)
@given("an absolute path fragment for phase analysis")
def step_absolute_path_fragment(context: Any) -> None:
"""Fragment with an absolute path that should be excluded by relative globs."""
context.phase_fragments = [
TieredFragment(
fragment_id="abs-skill",
content="skill content",
tier=ContextTier.HOT,
resource_id="local/repo-a",
project_name="local/ctx-app",
token_count=50,
metadata={
"path": "/app/.opencode/skills/SKILL.md",
"byte_size": 1000,
},
)
]
@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."""
strat = context.phase_result["phases"]["strategize"]
assert strat["fragment_count"] == 0, (
f"Expected 0 fragments (absolute path should be excluded by relative glob), "
f"got {strat['fragment_count']}"
)
1
@@ -31,10 +31,20 @@ def _path_matches(path: str, include_patterns: list[str]) -> bool:
def _matches_pattern(path_obj: PurePosixPath, pattern: str) -> bool:
"""Match with a small compatibility shim for ``**/`` zero-depth cases."""
return path_obj.match(pattern) or (
"**/" in pattern and path_obj.match(pattern.replace("**/", ""))
)
"""Match a path against a glob pattern, handling absolute vs relative paths.
Tries ``full_match()`` with the pattern as-is, then with a ``**/``
prefix so that relative globs (e.g. ``.opencode/*``) correctly match
absolute paths (e.g. ``/app/.opencode/skills/SKILL.md``). Also
handles the ``**/`` zero-depth compatibility shim.
"""
if path_obj.full_match(pattern):
return True
# Auto-prefix with **/ so relative patterns match absolute paths
if not pattern.startswith("**/") and path_obj.full_match(f"**/{pattern}"):
return True
# Zero-depth shim: "**/" in pattern but full_match already tried above
return bool("**/" in pattern and path_obj.full_match(pattern.replace("**/", "")))
def _extract_path(fragment: TieredFragment) -> str:
1
@@ -72,13 +72,32 @@ class ACMSExecutePhaseContextAssembler(ExecutePhaseContextAssembler):
@staticmethod
def _path_matches(path: str, include: list[str], exclude: list[str]) -> bool:
"""Return whether *path* passes include/exclude path globs."""
"""Return whether *path* passes include/exclude path globs.
Handles both absolute paths (e.g. ``/app/.opencode/skills/SKILL.md``)
and relative paths (e.g. ``src/foo.py``) against relative glob
patterns (e.g. ``.opencode/**``, ``src/**/*.py``).
Each pattern is tried with ``full_match()`` as-is (handles
relative paths and ``**`` patterns). If the pattern is not
already anchored with ``**/``, a second attempt prefixes
``**/`` so that relative globs also match absolute paths.
"""
pure_path = PurePath(path)
if include and not any(pure_path.full_match(pattern) for pattern in include):
def _matches_any(patterns: list[str]) -> bool:
for pattern in patterns:
if pure_path.full_match(pattern):
return True
if not pattern.startswith("**/") and pure_path.full_match(
f"**/{pattern}"
):
return True
return False
return not (
exclude and any(pure_path.full_match(pattern) for pattern in exclude)
)
if include and not _matches_any(include):
return False
return not (exclude and _matches_any(exclude))
@staticmethod
def _resource_matches(