;; lib/mod/schema.sx — report representation + Prolog fact generation. ;; ;; A report is a dict {:id :by :about :reason :evidence :attrs :signals :at}. ;; :evidence — accumulated {:kind :val} entries (human review, scanners) ;; :attrs — attribute names ("verified") for negation-as-failure conditions ;; :signals — weighted {:kind :weight} entries for aggregate scoring rules ;; :at — integer timestamp/tick (deterministic; supplied, not clock-read) ;; The engine derives keyword classifications from the reason text and projects ;; the report, its classifications, evidence, attributes, and signals into Prolog ;; facts that policy clauses match against. (define mod/mk-report (fn (id by about reason) {:attrs (list) :id id :signals (list) :by by :evidence (list) :about about :at 0 :reason reason})) (define mod/report-id (fn (r) (get r :id))) (define mod/report-by (fn (r) (get r :by))) (define mod/report-about (fn (r) (get r :about))) (define mod/report-reason (fn (r) (get r :reason))) (define mod/report-evidence (fn (r) (let ((e (get r :evidence))) (if (nil? e) (list) e)))) (define mod/report-attrs (fn (r) (let ((a (get r :attrs))) (if (nil? a) (list) a)))) (define mod/report-signals (fn (r) (let ((s (get r :signals))) (if (nil? s) (list) s)))) (define mod/report-at (fn (r) (let ((t (get r :at))) (if (nil? t) 0 t)))) (define mod/mk-evidence (fn (kind val) {:val val :kind kind})) (define mod/evidence-kind (fn (e) (get e :kind))) (define mod/evidence-val (fn (e) (get e :val))) (define mod/mk-signal (fn (kind weight) {:kind kind :weight weight})) (define mod/signal-kind (fn (s) (get s :kind))) (define mod/signal-weight (fn (s) (get s :weight))) (define mod/report* (fn (r evs attrs sigs at) {:attrs attrs :id (mod/report-id r) :signals sigs :by (mod/report-by r) :evidence evs :about (mod/report-about r) :at at :reason (mod/report-reason r)})) (define mod/with-evidence (fn (r evs) (mod/report* r evs (mod/report-attrs r) (mod/report-signals r) (mod/report-at r)))) (define mod/with-attrs (fn (r attrs) (mod/report* r (mod/report-evidence r) attrs (mod/report-signals r) (mod/report-at r)))) (define mod/with-signals (fn (r sigs) (mod/report* r (mod/report-evidence r) (mod/report-attrs r) sigs (mod/report-at r)))) (define mod/with-at (fn (r at) (mod/report* r (mod/report-evidence r) (mod/report-attrs r) (mod/report-signals r) at))) (define mod/attach-evidence (fn (r e) (mod/with-evidence r (append (mod/report-evidence r) (list e))))) (define mod/attach-attr (fn (r a) (mod/with-attrs r (append (mod/report-attrs r) (list a))))) (define mod/attach-signal (fn (r s) (mod/with-signals r (append (mod/report-signals r) (list s))))) ;; ── substring search (the prolog-loaded env lacks includes?; slice/len do work) ── (define mod/contains-at? (fn (hay needle hl nl pos) (if (< hl (+ pos nl)) false (if (= (slice hay pos (+ pos nl)) needle) true (mod/contains-at? hay needle hl nl (+ pos 1)))))) (define mod/str-contains? (fn (hay needle) (let ((hl (len hay)) (nl (len needle))) (if (= nl 0) true (mod/contains-at? hay needle hl nl 0))))) ;; ── evidence derivation (keyword classification) ── (define mod/spam-keywords (list "spam" "buy now" "click here" "free money" "viagra" "limited offer")) (define mod/abuse-keywords (list "abuse" "harassment" "threat" "slur" "hate speech")) (define mod/any? (fn (pred coll) (reduce (fn (acc x) (if acc acc (pred x))) false coll))) (define mod/reason-matches? (fn (reason kws) (let ((low (downcase reason))) (mod/any? (fn (k) (mod/str-contains? low k)) kws)))) (define mod/classify-keywords (fn (r) (let ((reason (mod/report-reason r)) (kinds (list))) (begin (when (mod/reason-matches? reason mod/spam-keywords) (append! kinds "spam")) (when (mod/reason-matches? reason mod/abuse-keywords) (append! kinds "abuse")) kinds)))) (define mod/report-count (fn (about reports) (reduce (fn (acc r) (if (= (mod/report-about r) about) (+ acc 1) acc)) 0 reports))) ;; ── Prolog fact projection ── (define mod/join-with (fn (sep items) (reduce (fn (acc x) (if (= acc "") x (str acc sep x))) "" items))) (define mod/pl-quote (fn (s) (str "'" s "'"))) (define mod/classification-facts (fn (id kinds) (mod/join-with "\n" (map (fn (k) (str "classification(" id ", " k ").")) kinds)))) (define mod/evidence-facts (fn (id evs) (mod/join-with "\n" (map (fn (e) (str "evidence(" id ", " (mod/pl-quote (mod/evidence-kind e)) ", " (mod/pl-quote (str (mod/evidence-val e))) ").")) evs)))) (define mod/attr-facts (fn (id attrs) (mod/join-with "\n" (map (fn (a) (str "attr(" id ", " a ").")) attrs)))) (define mod/signal-facts (fn (id sigs) (mod/join-with "\n" (map (fn (s) (str "signal(" id ", " (mod/pl-quote (mod/signal-kind s)) ", " (mod/signal-weight s) ").")) sigs)))) (define mod/report-facts (fn (r count) (let ((id (mod/report-id r)) (by (mod/pl-quote (mod/report-by r))) (about (mod/pl-quote (mod/report-about r)))) (let ((cls (mod/classification-facts id (mod/classify-keywords r))) (evs (mod/evidence-facts id (mod/report-evidence r))) (ats (mod/attr-facts id (mod/report-attrs r))) (sgs (mod/signal-facts id (mod/report-signals r)))) (mod/join-with "\n" (list (str "report(" id ", " by ", " about ").") (str "report_count(" about ", " count ").") cls evs ats sgs))))))