The CLI now clearly explains WHY each rule, task, skill, and command was included or excluded when running a task. This makes it much easier to understand the context assembly process and debug selector issues.
- Inclusion Explanations: Every included item now logs the reason it was selected
- Exclusion Explanations: Every excluded item now logs why it didn't match selectors
- Detailed Selector Matching: Shows exactly which selector key-value pairs matched or didn't match
pkg/codingcontext/selectors/selectors.go: RefactoredMatchesIncludes()to return(bool, string)with reasonpkg/codingcontext/context.go: Enhanced logging throughout to explain inclusion/exclusionpkg/codingcontext/selectors/selectors_test.go: Added comprehensive table-driven tests
./coding-context -C examples -s language=go -s env=dev my-taskOutput:
time=... level=INFO msg="Including task" name=my-task reason="task name matches 'my-task'" tokens=39
time=... level=INFO msg="Including rule file" path=.agents/rules/go-dev-rule.md reason="matched selectors: language=go, env=dev" tokens=44
time=... level=INFO msg="Including rule file" path=.agents/rules/generic-rule.md reason="no selectors specified (included by default)" tokens=45
time=... level=INFO msg="Discovered skill" name=go-testing reason="matched selectors: language=go" path=/path/to/skill/SKILL.md
./coding-context -C examples -s language=python -s env=prod my-taskOutput:
time=... level=INFO msg="Including task" name=my-task reason="task name matches 'my-task'" tokens=39
time=... level=INFO msg="Skipping file" path=.agents/rules/go-dev-rule.md reason="selectors did not match: language=go (expected language=python), env=dev (expected env=prod)"
time=... level=INFO msg="Including rule file" path=.agents/rules/python-prod-rule.md reason="matched selectors: language=python, env=prod" tokens=41
time=... level=INFO msg="Skipping skill" name=go-testing path=/path/to/skill/SKILL.md reason="selectors did not match: language=go (expected language=python)"
- Tasks:
Including taskwithnameandreason="task name matches '<task-name>'" - Rules:
Including rule filewithpath,reason, andtokens- With selectors:
reason="matched selectors: key1=value1, key2=value2" - Without selectors:
reason="no selectors specified (included by default)"
- With selectors:
- Skills:
Discovered skillwithname,path, andreason- With selectors:
reason="matched selectors: key1=value1, key2=value2" - Without selectors:
reason="no selectors specified (included by default)"
- With selectors:
- Commands:
Including commandwithname,path, andreason="referenced by slash command '/command-name'"
-
Rules:
Skipping filewithpathand detailed mismatch explanation- Single mismatch:
reason="selectors did not match: key=actual_value (expected key=expected_value)" - Multiple mismatches:
reason="selectors did not match: key1=actual1 (expected key1=expected1), key2=actual2 (expected key2=expected2)" - OR logic (multiple allowed values):
reason="selectors did not match: key=actual (expected key in [value1, value2, value3])"
- Single mismatch:
-
Skills:
Skipping skillwithname,path, and detailed mismatch explanation (same format as rules)
Returns whether the frontmatter matches all include selectors, along with a human-readable reason explaining the result.
Returns:
bool: true if all selectors match, false otherwisestring: reason explaining why (matched selectors or mismatch details)
Examples:
// Match case
match, reason := selectors.MatchesIncludes(frontmatter)
// match = true, reason = "matched selectors: language=go, env=dev"
// No match case
match, reason := selectors.MatchesIncludes(frontmatter)
// match = false, reason = "selectors did not match: language=python (expected language=go)"
// No selectors case
match, reason := selectors.MatchesIncludes(frontmatter)
// match = true, reason = "no selectors specified (included by default)"- Missing keys: If a selector key doesn't exist in frontmatter, it's allowed (not counted as a mismatch)
- Matching values: If a frontmatter value matches any selector value for that key, it matches (OR logic)
- Non-matching values: If a frontmatter value doesn't match any selector value for that key, it doesn't match
TestSelectorMap_MatchesIncludes: 19 test cases covering basic matching/non-matching scenariosTestSelectorMap_MatchesIncludesReasons: 9 test cases covering reason explanations with various scenarios- All tests use table-driven test pattern (project standard)
- Tests cover: single selectors, multiple selectors, array selectors, OR logic, edge cases, both match and no-match cases
# Test the selectors package
go test -v ./pkg/codingcontext/selectors/
# Test the main context package
go test -v ./pkg/codingcontext/
# Run all tests
go test -v ./...- Debugging: Instantly see why rules/skills aren't being included
- Transparency: Understand exactly how selector matching works
- Configuration Validation: Verify that your frontmatter and selectors are set up correctly
- Learning: New users can understand the system by observing the logs
- Efficiency: Single method call returns both match result and reason (no duplicate work)
This feature is fully backwards compatible:
- No breaking changes to APIs or behavior
- Only adds additional logging information
- All existing tests pass
- Existing rule/task/skill files work without modification
Potential improvements for future versions:
- Add a
--quietflag to suppress inclusion/exclusion logging - Add a
--explainflag to show even more detailed selector evaluation - Color-code inclusion (green) vs exclusion (yellow) messages in terminal output
- Add summary statistics (e.g., "Included 5 rules, skipped 3 rules")