mod: Ext 1 — negation-as-failure conditions (:not / :attr), 146/146
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 44s

Report attributes (:attrs) project to attr(Id, name) facts; policy gains (:attr x)
and (:not <cond>) conditions. The Prolog substrate exposes negation as a functor
not(Goal) (the prefix \+ operator doesn't parse here). Closed-world example:
hide spam unless author verified. Default policy untouched — feature proven via
custom rule sets, so all 132 base tests stay green. +14 extension tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-06 17:59:01 +00:00
parent ee9851c063
commit 2ea87796a1
7 changed files with 218 additions and 15 deletions

View File

@@ -1,12 +1,13 @@
;; lib/mod/schema.sx — report representation + Prolog fact generation.
;;
;; A report is a dict {:id :by :about :reason :evidence}. :evidence is a list of
;; accumulated evidence entries {:kind :val} (human review, automated scanners,
;; etc.). The engine derives keyword classifications from the reason text and
;; projects the report, its classifications, and its accumulated evidence into
;; Prolog facts that policy clauses match against.
;; A report is a dict {:id :by :about :reason :evidence :attrs}. :evidence is a
;; list of accumulated evidence entries {:kind :val} (human review, automated
;; scanners). :attrs is a list of attribute names (e.g. "verified") used by
;; negation-as-failure conditions. The engine derives keyword classifications
;; from the reason text and projects the report, its classifications, evidence,
;; and attributes into Prolog facts that policy clauses match against.
(define mod/mk-report (fn (id by about reason) {:id id :by by :evidence (list) :about about :reason reason}))
(define mod/mk-report (fn (id by about reason) {:attrs (list) :id id :by by :evidence (list) :about about :reason reason}))
(define mod/report-id (fn (r) (get r :id)))
(define mod/report-by (fn (r) (get r :by)))
@@ -17,11 +18,23 @@
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/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/with-evidence (fn (r evs) {:id (mod/report-id r) :by (mod/report-by r) :evidence evs :about (mod/report-about r) :reason (mod/report-reason r)}))
(define mod/report* (fn (r evs attrs) {:attrs attrs :id (mod/report-id r) :by (mod/report-by r) :evidence evs :about (mod/report-about r) :reason (mod/report-reason r)}))
(define
mod/with-evidence
(fn (r evs) (mod/report* r evs (mod/report-attrs r))))
(define
mod/with-attrs
(fn (r attrs) (mod/report* r (mod/report-evidence r) attrs)))
(define
mod/attach-evidence
@@ -29,6 +42,10 @@
(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)))))
;; ── substring search (the prolog-loaded env lacks includes?; slice/len do work) ──
(define
@@ -139,6 +156,12 @@
")."))
evs))))
(define
mod/attr-facts
(fn
(id attrs)
(mod/join-with "\n" (map (fn (a) (str "attr(" id ", " a ").")) attrs))))
(define
mod/report-facts
(fn
@@ -149,11 +172,13 @@
(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))))
(evs (mod/evidence-facts id (mod/report-evidence r)))
(ats (mod/attr-facts id (mod/report-attrs r))))
(mod/join-with
"\n"
(list
(str "report(" id ", " by ", " about ").")
(str "report_count(" about ", " count ").")
cls
evs))))))
evs
ats))))))