Files
deklarix/internal/rules/evaluate.go
noroot bec34b5988 feat: add rules engine (internal/rules) with first two disclosure rules
internal/rules implements Stufe 2 from the core principle: the LLM
extracts facts, this deterministic engine judges them against versioned
YAML rules. Facts/Condition/Rule/Finding types, a loader that refuses to
load on a missing id/version or a duplicate rule id rather than silently
skipping a bad file, and Evaluate() matching facts against rules.

Two real rules grounded in verified research (see rules/OPEN.md for the
open questions that surfaced along the way):
- WK-001: no disclosure at all despite consideration (§ 5a Abs. 4 UWG,
  § 22 Abs. 1 MStV)
- WK-004: disclosure present but hidden behind a "mehr anzeigen" cut
  (§ 5a Abs. 4 UWG, Leitfaden der Medienanstalten, LG Köln 12.05.2026)

The two are deliberately disjoint (WK-004 requires disclosure_present=
true) so a post with no disclosure at all doesn't double-fire both
rules. Golden suite in testdata/golden/ covers both rules plus two clean
cases; it's this suite, not the UI, that's the actual asset per
CLAUDE.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 13:35:07 +02:00

31 lines
725 B
Go

package rules
// Finding ist das Ergebnis einer einzelnen zutreffenden Regel.
type Finding struct {
RuleID string
RuleVersion int
Severity Severity
Title string
Fix string
Sources []string
}
// Evaluate prüft alle Regeln gegen f und liefert ein Finding für jede
// zutreffende Regel. Reihenfolge folgt der Reihenfolge von rules.
func Evaluate(rules []Rule, f Facts) []Finding {
var findings []Finding
for _, r := range rules {
if r.Condition.Matches(f) {
findings = append(findings, Finding{
RuleID: r.ID,
RuleVersion: r.Version,
Severity: r.Severity,
Title: r.Title,
Fix: r.Fix,
Sources: r.Sources,
})
}
}
return findings
}