;; lib/mod/trace.sx — policy dry-run diagnostics. ;; ;; decide-report returns the winning rule; a policy author debugging "why didn't ;; my rule fire?" needs the whole picture. mod/trace-rules evaluates a report ;; against every rule and reports each rule's proved/unproved status plus its ;; goal-by-goal derivation — so an unproved rule shows exactly which goal failed. ;; The winner is the first proved rule (same precedence as the engine). (define mod/trace-rules (fn (r reports rules) (let ((count (mod/report-count (mod/report-about r) reports)) (id (mod/report-id r))) (let ((db (pl-load (mod/build-program r count rules)))) (let ((proved-names (map (fn (s) (dict-get s "Rule")) (pl-query-all db (str "policy_action(" id ", _, Rule)"))))) (map (fn (rule) (let ((nm (mod/rule-name rule))) {:proved (mod/member? nm proved-names) :goals (mod/proof-goals db id (mod/rule-when rule)) :action (mod/rule-action rule) :rule nm})) rules)))))) (define mod/first-proved (fn (trace) (reduce (fn (acc t) (if (nil? acc) (if (get t :proved) t acc) acc)) nil trace))) (define mod/proved-rules (fn (trace) (reduce (fn (acc t) (if (get t :proved) (append acc (list (get t :rule))) acc)) (list) trace))) (define mod/trace-row (fn (t) (str (if (get t :proved) "[fires] " "[ - ] ") (get t :rule) " → " (get t :action)))) (define mod/trace-report (fn (trace) (mod/join-with "\n" (map mod/trace-row trace))))