;; lib/mod/tests/audit.sx — Phase 2: evidence accumulation + proof tree + audit. (define mod-aud-count 0) (define mod-aud-pass 0) (define mod-aud-fail 0) (define mod-aud-failures (list)) (define mod-aud-test! (fn (name got expected) (begin (set! mod-aud-count (+ mod-aud-count 1)) (if (= got expected) (set! mod-aud-pass (+ mod-aud-pass 1)) (begin (set! mod-aud-fail (+ mod-aud-fail 1)) (append! mod-aud-failures (str name "\n expected: " expected "\n got: " got))))))) (define mod-aud-decide1 (fn (r) (mod/decide-report r (list r) mod/default-rules))) ;; ── proof tree: keyword classification ── (define mod-aud-spam (mod-aud-decide1 (mod/mk-report "r1" "alice" "bob" "this is spam"))) (define mod-aud-spam-goals (get (get mod-aud-spam :proof) :goals)) (mod-aud-test! "spam proof has one goal" (len mod-aud-spam-goals) 1) (mod-aud-test! "spam proof goal text" (get (first mod-aud-spam-goals) :goal) "classification(r1, spam)") (mod-aud-test! "spam proof goal solved" (get (first mod-aud-spam-goals) :solved) true) ;; ── proof tree: count rule with real bindings ── (define mod-aud-rep-r (mod/mk-report "r3" "ann" "dave" "x")) (define mod-aud-rep (mod/decide-report mod-aud-rep-r (list mod-aud-rep-r mod-aud-rep-r mod-aud-rep-r) mod/default-rules)) (define mod-aud-rep-goals (get (get mod-aud-rep :proof) :goals)) (define mod-aud-rep-binds (get (first mod-aud-rep-goals) :bindings)) (mod-aud-test! "count proof goal solved" (get (first mod-aud-rep-goals) :solved) true) (mod-aud-test! "count proof binding N" (dict-get mod-aud-rep-binds "N") "3") (mod-aud-test! "count proof binding S (subject)" (dict-get mod-aud-rep-binds "S") "dave") ;; ── proof tree: default keep has a 'true' goal ── (define mod-aud-keep (mod-aud-decide1 (mod/mk-report "rk" "a" "b" "a fine post"))) (define mod-aud-keep-goals (get (get mod-aud-keep :proof) :goals)) (mod-aud-test! "keep proof goal text true" (get (first mod-aud-keep-goals) :goal) "true") (mod-aud-test! "keep proof goal solved" (get (first mod-aud-keep-goals) :solved) true) ;; ── evidence accumulation drives a rule ── (define mod-aud-rev-r (mod/attach-evidence (mod/mk-report "re" "a" "carol" "neutral") (mod/mk-evidence "confirmed-abuse" "human"))) (define mod-aud-rev (mod-aud-decide1 mod-aud-rev-r)) (mod-aud-test! "evidence has length 1" (len (mod/report-evidence mod-aud-rev-r)) 1) (mod-aud-test! "evidence reviewer-remove → remove" (get mod-aud-rev :action) "remove") (mod-aud-test! "evidence reviewer-remove rule" (get mod-aud-rev :rule) "reviewer-remove") (mod-aud-test! "evidence proof goal solved" (get (first (get (get mod-aud-rev :proof) :goals)) :solved) true) (mod-aud-test! "no evidence → not reviewer-remove" (get (mod-aud-decide1 (mod/mk-report "rn" "a" "b" "neutral")) :rule) "default-keep") ;; ── append-only audit log via the api ── (mod/reset!) (mod/report "alice" "bob" "this is spam") (mod/report "carol" "eve" "fine post") (define mod-aud-d1 (mod/decide "r1")) (define mod-aud-d2 (mod/decide "r2")) (mod-aud-test! "two decisions logged" (mod/audit-count) 2) (mod-aud-test! "first entry seq 1" (get (first (mod/audit-all)) :seq) 1) (mod-aud-test! "audit r1 returns one entry" (len (mod/audit "r1")) 1) (mod-aud-test! "audit r1 action matches decision" (get (first (mod/audit "r1")) :action) (get mod-aud-d1 :action)) (mod-aud-test! "audit r1 rule matches decision" (get (first (mod/audit "r1")) :rule) "spam-hide") (mod-aud-test! "audit r1 entry carries proof goals" (len (get (get (first (mod/audit "r1")) :proof) :goals)) 1) (mod-aud-test! "audit r2 keep" (get (first (mod/audit "r2")) :action) "keep") (mod-aud-test! "audit unknown report → empty" (mod/audit "r99") (list)) ;; ── append-only: re-deciding appends, never mutates ── (define mod-aud-d1b (mod/decide "r1")) (mod-aud-test! "re-decide appends (count 3)" (mod/audit-count) 3) (mod-aud-test! "audit r1 now has 2 entries" (len (mod/audit "r1")) 2) (mod-aud-test! "audit r1 seqs monotonic" (get (nth (mod/audit "r1") 1) :seq) 3) (mod-aud-test! "audit-latest r1 is seq 3" (get (mod/audit-latest "r1") :seq) 3) (mod-aud-test! "first r1 entry unchanged (still seq 1)" (get (first (mod/audit "r1")) :seq) 1) ;; ── evidence snapshot captured at decision time ── (mod/add-evidence "r2" "confirmed-abuse" "human") (define mod-aud-d2b (mod/decide "r2")) (mod-aud-test! "post-evidence decision flips to remove" (get mod-aud-d2b :action) "remove") (mod-aud-test! "audit snapshot records evidence kind" (mod/evidence-kind (first (get (mod/audit-latest "r2") :evidence))) "confirmed-abuse") (mod-aud-test! "earlier r2 entry had empty evidence snapshot" (len (get (first (mod/audit "r2")) :evidence)) 0) (define mod-audit-tests-run! (fn () {:failures mod-aud-failures :total mod-aud-count :passed mod-aud-pass :failed mod-aud-fail}))