;; lib/mod/policy.sx — moderation rules → Prolog clauses. ;; ;; A rule is {:name :action :when}. :when is a list of condition forms; each ;; compiles to a Prolog goal. Rule order is precedence: the engine queries with ;; pl-query-one, so the first clause that proves wins. The final default rule has ;; an empty body (true) so every report yields at least :keep — "no rule matched" ;; is a real result, not a query failure. (define mod/mk-rule (fn (name action conds) {:when conds :name name :action action})) (define mod/rule-name (fn (r) (get r :name))) (define mod/rule-action (fn (r) (get r :action))) (define mod/rule-when (fn (r) (get r :when))) (define mod/default-rules (list (mod/mk-rule "spam-hide" :hide (list (list :classification "spam"))) (mod/mk-rule "abuse-remove" :remove (list (list :classification "abuse"))) (mod/mk-rule "repeated-escalate" :escalate (list (list :count-at-least 3))) (mod/mk-rule "default-keep" :keep (list)))) ;; ── condition → Prolog goal ── ;; ;; (:classification "spam") → classification(Id, spam) ;; (:count-at-least 3) → report(Id, B, S), report_count(S, N), N >= 3 (define mod/cond->goal (fn (c) (let ((tag (first c))) (cond ((= tag :classification) (str "classification(Id, " (nth c 1) ")")) ((= tag :count-at-least) (str "report(Id, B, S), report_count(S, N), N >= " (nth c 1))) (true "true"))))) (define mod/conds->body (fn (conds) (if (empty? conds) "true" (mod/join-with ", " (map mod/cond->goal conds))))) (define mod/rule->clause (fn (r) (str "policy_action(Id, " (mod/rule-action r) ", '" (mod/rule-name r) "') :- " (mod/conds->body (mod/rule-when r)) "."))) (define mod/rules->program (fn (rules) (mod/join-with "\n" (map mod/rule->clause rules))))