Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 1m7s
Reports carry an :evidence list, asserted as evidence/3 facts; reviewer-remove rule (highest precedence) lets human review override classification. Proof tree built constructively by re-querying each rule body goal against the same DB with the report id bound, so derivations carry real unification bindings. Append-only audit log records decision + proof + evidence snapshot per decide, monotonic seq, never mutates prior entries. +29 audit tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
188 lines
5.0 KiB
Plaintext
188 lines
5.0 KiB
Plaintext
;; 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}))
|