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>
65 lines
2.3 KiB
Plaintext
65 lines
2.3 KiB
Plaintext
;; lib/mod/engine.sx — decide a report by querying the policy program.
|
|
;;
|
|
;; build-program assembles the report's facts plus the compiled policy clauses;
|
|
;; decide-report runs the Prolog query and returns a decision. A decision is a
|
|
;; proof, not a bare keyword: it carries the matching rule, the conditions it
|
|
;; required, the evidence that satisfied them, and a derivation — the proof tree.
|
|
;;
|
|
;; The proof tree is built constructively: for the matching rule, each body goal
|
|
;; is re-queried against the same DB with the report id bound, recording the goal
|
|
;; text, whether it was solved, and the bindings that satisfied it. That is a
|
|
;; genuine derivation drawn from the Prolog database, ready for the audit trail.
|
|
|
|
(define
|
|
mod/find-rule
|
|
(fn
|
|
(rules name)
|
|
(reduce
|
|
(fn
|
|
(acc r)
|
|
(if (nil? acc) (if (= (mod/rule-name r) name) r acc) acc))
|
|
nil
|
|
rules)))
|
|
|
|
(define
|
|
mod/build-program
|
|
(fn
|
|
(r count rules)
|
|
(str (mod/report-facts r count) "\n" (mod/rules->program rules))))
|
|
|
|
(define
|
|
mod/proof-goals
|
|
(fn
|
|
(db id conds)
|
|
(if
|
|
(empty? conds)
|
|
(list {:solved true :goal "true" :bindings {}})
|
|
(map
|
|
(fn
|
|
(c)
|
|
(let
|
|
((g (mod/cond->goal c id)))
|
|
(let ((sols (pl-query-all db g))) {:solved (if (empty? sols) false true) :goal g :bindings (if (empty? sols) {} (first sols))})))
|
|
conds))))
|
|
|
|
(define
|
|
mod/decide-report
|
|
(fn
|
|
(r reports rules)
|
|
(let
|
|
((count (mod/report-count (mod/report-about r) reports))
|
|
(kinds (mod/classify-keywords r))
|
|
(id (mod/report-id r)))
|
|
(let
|
|
((program (mod/build-program r count rules)))
|
|
(let
|
|
((db (pl-load program)))
|
|
(let
|
|
((sol (pl-query-one db (str "policy_action(" id ", Action, Rule)"))))
|
|
(if
|
|
(nil? sol)
|
|
{:action "keep" :proof {:goals (list) :evidence kinds :conditions (list) :rule "none" :count count} :report-id id :rule "none"}
|
|
(let
|
|
((rname (dict-get sol "Rule")))
|
|
(let ((rule (mod/find-rule rules rname))) {:action (mod/rule-action rule) :proof {:goals (mod/proof-goals db id (mod/rule-when rule)) :evidence kinds :conditions (mod/rule-when rule) :rule rname :count count} :report-id id :rule rname})))))))))
|