Epic 9 Task 9.1 defines the schema and precedence contract for conditional rule injection.
Rules are discovered from layered scopes in deterministic order:
- user scope:
~/.config/opencode/rules/**/*.md - project scope:
.opencode/rules/**/*.md
Project scope has higher precedence when two rules have equivalent priority and target overlap.
Each rule file is markdown with YAML frontmatter.
Required frontmatter fields:
description: short purpose summarypriority: integer from0to100(higher applies first)
Optional frontmatter fields:
globs: list of file globs where rule appliesalwaysApply: boolean forcing rule application regardless of file pathid: stable rule identifier (fallback: normalized file stem)tags: list of category tags for diagnostics/reporting
Rule body (markdown after frontmatter) is the instruction payload injected at runtime.
- If
alwaysApplyistrue, the rule always applies. - Else if
globsis present, any glob match applies the rule. - Else the rule is considered inactive by default.
Glob matching uses workspace-relative POSIX-style paths.
Sort and merge rules by deterministic key:
- descending
priority - scope precedence (
projectbeforeuser) - lexical
id
Conflicts are resolved by first-writer-wins over normalized rule ids after sorting.
Diagnostics must surface:
- winning rule id and source
- overridden/conflicting rule ids
- effective ordered rule stack for any target path
Planned config controls:
rules.enabled(defaulttrue)rules.disabled_ids(list)rules.extra_paths(additional discovery roots)
Reject and report rules when:
- required frontmatter keys are missing
priorityis out of range or non-numericglobsis not a list of stringsalwaysApplyis non-boolean
Validation errors should include rule path and actionable remediation.