Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 54s
Pure SX state machine (lib/mod/lifecycle.sx) over the engine: open→triaged→decided→appealed→final, transition table guards illegal moves. Auto-tier resolves terminal actions; escalate parks at human-tier (resolve blocked until review supplies evidence). Appeal re-runs the engine — new exonerated-keep rule at top precedence lets exoneration override a prior hide. Api façade (mod/triage/resolve/review/appeal/finalize) over a case registry, logging committed decisions to the audit trail. +46 escalation tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
96 lines
2.8 KiB
Plaintext
96 lines
2.8 KiB
Plaintext
;; 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.
|
|
;;
|
|
;; cond->goal takes an id-term so the same condition can be compiled with the
|
|
;; head variable "Id" (for clause bodies) or a concrete report id (for proof-tree
|
|
;; goal-by-goal re-querying in the engine).
|
|
;;
|
|
;; Precedence (top wins): exoneration evidence (appeal override) > confirmed-abuse
|
|
;; evidence (human review) > spam/abuse classification > repeated-report count >
|
|
;; default keep.
|
|
|
|
(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
|
|
"exonerated-keep"
|
|
:keep (list (list :evidence "exonerated")))
|
|
(mod/mk-rule
|
|
"reviewer-remove"
|
|
:remove (list (list :evidence "confirmed-abuse")))
|
|
(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)
|
|
;; (:evidence "kind") → evidence(Id, 'kind', _)
|
|
;; (:count-at-least 3) → report(Id, B, S), report_count(S, N), N >= 3
|
|
|
|
(define
|
|
mod/cond->goal
|
|
(fn
|
|
(c idterm)
|
|
(let
|
|
((tag (first c)))
|
|
(cond
|
|
((= tag :classification)
|
|
(str "classification(" idterm ", " (nth c 1) ")"))
|
|
((= tag :evidence)
|
|
(str
|
|
"evidence("
|
|
idterm
|
|
", "
|
|
(mod/pl-quote (nth c 1))
|
|
", _)"))
|
|
((= tag :count-at-least)
|
|
(str
|
|
"report("
|
|
idterm
|
|
", B, S), report_count(S, N), N >= "
|
|
(nth c 1)))
|
|
(true "true")))))
|
|
|
|
(define
|
|
mod/conds->body
|
|
(fn
|
|
(conds idterm)
|
|
(if
|
|
(empty? conds)
|
|
"true"
|
|
(mod/join-with ", " (map (fn (c) (mod/cond->goal c idterm)) 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) "Id")
|
|
".")))
|
|
|
|
(define
|
|
mod/rules->program
|
|
(fn (rules) (mod/join-with "\n" (map mod/rule->clause rules))))
|