Problem
The rubric evaluation system has two tiers: hardcoded deterministic checkers (Phase 2) and LLM judgment (Phase 3). There's a gap between them — policy constraints that are programmatically verifiable but user-configurable. Examples:
These currently fall through `match_rubric_item()` → `None` and get sent to the LLM, wasting tokens on something a SQL query or metadata check can answer definitively.
Solution
Add a policy rubric matcher that recognizes structured constraint syntax in `evaluation_rubric` items and creates parameterized checkers at runtime. Runs at tier 1 alongside existing deterministic checks — no LLM cost.
Constraint Syntax
Rubric items with structured patterns are recognized and handled programmatically:
```yaml evaluation_rubric: # Count constraints on metadata fields - "metadata.source_url count >= 2" - "metadata.reviewers count >= 2"
# Link/relation constraints - "links.actor count >= 1" - "outlinks count >= 3"
# Tag constraints - "tags count >= 2"
# Body section requirements - "body.section 'References' required"
# Status transition gates (pairs with lifecycle/kanban) - "status proposed->accepted requires metadata.reviewers count >= 2" ```
Architecture
1. Policy matcher in `rubric_checkers.py`: regex-based pattern recognition for structured syntax, returns parameterized checker functions 2. Precedence: `is_already_covered()` → `match_rubric_item()` → `match_policy_rule()` → falls through to LLM 3. `_collect_judgment_items()` updated to also filter out policy-matched items 4. Free-text rubric items continue to LLM evaluation unchanged
Status Transition Gates
The `status X->Y requires
```yaml
In kb.yaml type overrides
types: adr: evaluation_rubric: - "status proposed->accepted requires metadata.reviewers count >= 2" - "status proposed->accepted requires body.section 'Alternatives' required" ```This runs during `pyrite ci` and QA validation — if an ADR is in `accepted` status but doesn't meet the gate criteria, it produces a `rubric_violation`.