fix(acms): normalize context path matching for absolute paths in _path_matches #10975
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
|
||||
@tdd_issue @tdd_issue_10972
|
||||
|
HAL9001
commented
BLOCKER — These new scenarios for Additionally, per the TDD bug fix workflow, regression test scenarios for a bug fix must be tagged with **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
```
HAL9001
commented
BLOCKER - Tag indentation defect: This tag (line 49) is at column 0 (zero indentation), but the Current (broken): Required fix: 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 **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
|
||||
|
HAL9001
commented
BLOCKER — Missing Per the TDD bug fix workflow, all BDD scenarios added as regression tests for bug #10972 must be tagged with Required change: Apply the same two tags to all 5 new scenarios in this file. Automated by CleverAgents Bot **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
HAL9001
commented
BLOCKER — Missing @tdd_issue @tdd_issue_10972 tags All new BDD scenarios for a Type/Bug fix must carry both Required: add both tags before each of the 5 new scenarios in this file: Automated by CleverAgents Bot **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
|
||||
|
||||
|
HAL9001
commented
BLOCKER — Same as above: this scenario must be tagged **BLOCKER** — Same as above: this scenario must be tagged `@tdd_issue @tdd_issue_10972` per the TDD bug fix workflow for regression tracking.
HAL9001
commented
BLOCKER — Missing @tdd_issue @tdd_issue_10972 tags This regression scenario for bug #10972 must carry both Automated by CleverAgents Bot **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
|
||||
|
HAL9001
commented
BLOCKER — Missing This scenario was added as a regression test for bug #10972. Per the TDD bug fix workflow it must carry both Required change: Automated by CleverAgents Bot **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
HAL9001
commented
BLOCKER — Missing This regression scenario for bug #10972 must carry both This was requested in reviews 7800 and 7809 and is still unaddressed. Automated by CleverAgents Bot **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']}"
|
||||
)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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(
|
||||
|
||||
BLOCKER — Missing
@tdd_issue @tdd_issue_10972tags (third request)Issue #10972 is
Type/Bug. All 5 new BDD scenarios in this file must carry both@tdd_issueand@tdd_issue_10972tags per the TDD bug fix workflow in CONTRIBUTING.md.Required format for each new scenario:
Apply the same two tags to all 5 new scenarios:
epcov path matches absolute path against relative include globepcov path matches absolute path against relative exclude globepcov path matches absolute path against relative include glob with wildcardepcov path matches absolute path not matching relative include globepcov relative path is excluded by trailing ** globThis was requested in reviews 7800 and 7809 and is still unaddressed.
Automated by CleverAgents Bot
Supervisor: PR Review | Agent: pr-review-worker
BLOCKER — Tag indentation inconsistency causes Behave parse failure
The
@tdd_issue @tdd_issue_10972tag 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_testshas been failing since this squashed commit.Required fix — add 2 spaces of leading indentation before the tag:
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