;; lib/mod/offenders.sx — repeat-offender escalation (audit log as evidence).
;;
;; The append-only audit trail is itself a source of evidence: a subject already
;; sanctioned several times is a repeat offender. mod/decide-escalating decides a
;; report normally, then — if the action is a sanction and the subject has at
;; least k PRIOR sanctions in the audit log — upgrades it to :ban. This is the one
;; place a decision depends on history beyond the single report, and it reads that
;; history from the audit log rather than re-deriving it.

(define
  mod/sanction?
  (fn
    (action)
    (mod/any? (fn (a) (= a action)) (list "hide" "remove" "ban"))))

;; count of prior sanctioning decisions in the audit log about a subject
(define
  mod/subject-sanctions
  (fn
    (subject)
    (reduce
      (fn
        (acc e)
        (let
          ((r (mod/get-report (get e :report-id))))
          (if
            (nil? r)
            acc
            (if
              (if
                (= (mod/report-about r) subject)
                (mod/sanction? (get e :action))
                false)
              (+ acc 1)
              acc))))
      0
      (mod/audit-all))))

(define
  mod/repeat-offender?
  (fn (subject k) (<= k (mod/subject-sanctions subject))))

(define
  mod/decide-escalating
  (fn
    (id k)
    (let
      ((r (mod/get-report id)))
      (if
        (nil? r)
        nil
        (let
          ((priors (mod/subject-sanctions (mod/report-about r))))
          (let
            ((d (mod/decide id)))
            (if
              (if (mod/sanction? (get d :action)) (<= k priors) false)
              {:action "ban" :proof {:goals (get (get d :proof) :goals) :prior-sanctions priors :evidence (get (get d :proof) :evidence) :conditions (list) :rule "repeat-offender-ban" :count (get (get d :proof) :count)} :report-id id :rule "repeat-offender-ban" :strategy "escalating"}
              d)))))))
