;; lib/mod/tests/temporal.sx — Ext 12: burst detection over a time window. (define mod-tm-count 0) (define mod-tm-pass 0) (define mod-tm-fail 0) (define mod-tm-failures (list)) (define mod-tm-test! (fn (name got expected) (begin (set! mod-tm-count (+ mod-tm-count 1)) (if (= got expected) (set! mod-tm-pass (+ mod-tm-pass 1)) (begin (set! mod-tm-fail (+ mod-tm-fail 1)) (append! mod-tm-failures (str name "\n expected: " expected "\n got: " got))))))) (define mod-tm-at (fn (id about t) (mod/with-at (mod/mk-report id "u" about "off-topic") t))) (define mod-tm-rules (list (mod/mk-rule "burst-hide" :hide (list (list :burst-at-least 3))) (mod/mk-rule "default-keep" :keep (list)))) ;; ── window-count helper ── (define mod-tm-burst (list (mod-tm-at "r1" "bob" 10) (mod-tm-at "r2" "bob" 11) (mod-tm-at "r3" "bob" 12))) (define mod-tm-slow (list (mod-tm-at "r1" "bob" 1) (mod-tm-at "r2" "bob" 2) (mod-tm-at "r3" "bob" 12))) (mod-tm-test! "window-count: all 3 within window" (mod/window-count "bob" mod-tm-burst 12 5) 3) (mod-tm-test! "window-count: only 1 within window" (mod/window-count "bob" mod-tm-slow 12 5) 1) (mod-tm-test! "window-count: subject filter" (mod/window-count "eve" mod-tm-burst 12 5) 0) ;; ── burst fires; slow accumulation does not ── (mod-tm-test! "burst (3 in window) → hide" (get (mod/decide-temporal (first mod-tm-burst) mod-tm-burst mod-tm-rules 12 5) :action) "hide") (mod-tm-test! "slow accumulation (1 in window) → keep" (get (mod/decide-temporal (first mod-tm-slow) mod-tm-slow mod-tm-rules 12 5) :action) "keep") ;; ── contrast: the plain count rule fires on BOTH (3 total reports) ── (mod-tm-test! "count rule fires on slow case (distinct from burst)" (get (mod/decide-report (first mod-tm-slow) mod-tm-slow mod/default-rules) :action) "escalate") ;; ── decision shape ── (define mod-tm-d (mod/decide-temporal (first mod-tm-burst) mod-tm-burst mod-tm-rules 12 5)) (mod-tm-test! "burst decision rule" (get mod-tm-d :rule) "burst-hide") (mod-tm-test! "burst decision tagged strategy" (get mod-tm-d :strategy) "temporal") (mod-tm-test! "burst recorded in proof" (get (get mod-tm-d :proof) :burst) 3) (mod-tm-test! "burst proof goal solved" (get (first (get (get mod-tm-d :proof) :goals)) :solved) true) ;; ── window boundary is inclusive ── (define mod-tm-edge (list (mod-tm-at "r1" "bob" 7) (mod-tm-at "r2" "bob" 8) (mod-tm-at "r3" "bob" 9))) (mod-tm-test! "window boundary inclusive (now-window = at)" (mod/window-count "bob" mod-tm-edge 12 5) 3) ;; ── schema :at round-trips and survives evidence attach ── (mod-tm-test! "report-at reads timestamp" (mod/report-at (mod-tm-at "r1" "bob" 42)) 42) (mod-tm-test! "default report-at is 0" (mod/report-at (mod/mk-report "r1" "a" "b" "x")) 0) (mod-tm-test! "attach-evidence preserves :at" (mod/report-at (mod/attach-evidence (mod-tm-at "r1" "bob" 42) (mod/mk-evidence "k" "v"))) 42) ;; ── cond->goal :burst-at-least ── (mod-tm-test! "cond->goal :burst-at-least" (mod/cond->goal (list :burst-at-least 3) "Id") "report(Id, _, Sb), burst_count(Sb, Nb), Nb >= 3") (define mod-temporal-tests-run! (fn () {:failures mod-tm-failures :total mod-tm-count :passed mod-tm-pass :failed mod-tm-fail}))