Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 54s
Pure SX state machine (lib/mod/lifecycle.sx) over the engine: open→triaged→decided→appealed→final, transition table guards illegal moves. Auto-tier resolves terminal actions; escalate parks at human-tier (resolve blocked until review supplies evidence). Appeal re-runs the engine — new exonerated-keep rule at top precedence lets exoneration override a prior hide. Api façade (mod/triage/resolve/review/appeal/finalize) over a case registry, logging committed decisions to the audit trail. +46 escalation tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
280 lines
7.4 KiB
Plaintext
280 lines
7.4 KiB
Plaintext
;; lib/mod/tests/escalation.sx — Phase 3: lifecycle state machine + escalation.
|
|
|
|
(define mod-esc-count 0)
|
|
(define mod-esc-pass 0)
|
|
(define mod-esc-fail 0)
|
|
(define mod-esc-failures (list))
|
|
|
|
(define
|
|
mod-esc-test!
|
|
(fn
|
|
(name got expected)
|
|
(begin
|
|
(set! mod-esc-count (+ mod-esc-count 1))
|
|
(if
|
|
(= got expected)
|
|
(set! mod-esc-pass (+ mod-esc-pass 1))
|
|
(begin
|
|
(set! mod-esc-fail (+ mod-esc-fail 1))
|
|
(append!
|
|
mod-esc-failures
|
|
(str name "\n expected: " expected "\n got: " got)))))))
|
|
|
|
;; ── transition table guard ──
|
|
|
|
(mod-esc-test!
|
|
"open → triaged allowed"
|
|
(mod/lc-can-transition? "open" "triaged")
|
|
true)
|
|
(mod-esc-test!
|
|
"triaged → decided allowed"
|
|
(mod/lc-can-transition? "triaged" "decided")
|
|
true)
|
|
(mod-esc-test!
|
|
"decided → appealed allowed"
|
|
(mod/lc-can-transition? "decided" "appealed")
|
|
true)
|
|
(mod-esc-test!
|
|
"appealed → final allowed"
|
|
(mod/lc-can-transition? "appealed" "final")
|
|
true)
|
|
(mod-esc-test!
|
|
"open → decided rejected"
|
|
(mod/lc-can-transition? "open" "decided")
|
|
false)
|
|
(mod-esc-test!
|
|
"triaged → final rejected"
|
|
(mod/lc-can-transition? "triaged" "final")
|
|
false)
|
|
(mod-esc-test!
|
|
"final is terminal"
|
|
(mod/lc-can-transition? "final" "open")
|
|
false)
|
|
|
|
;; ── initial state ──
|
|
|
|
(define
|
|
mod-esc-c0
|
|
(mod/mk-case (mod/mk-report "r1" "alice" "bob" "this is spam")))
|
|
(mod-esc-test! "new case is open" (mod/case-state mod-esc-c0) "open")
|
|
(mod-esc-test! "new case has no decision" (mod/case-decision mod-esc-c0) nil)
|
|
|
|
;; ── auto-tier: spam triages + resolves to decided/hide ──
|
|
|
|
(define
|
|
mod-esc-spam-rep
|
|
(list (mod/mk-report "r1" "alice" "bob" "this is spam")))
|
|
(define
|
|
mod-esc-t1
|
|
(mod/case-triage mod-esc-c0 mod-esc-spam-rep mod/default-rules))
|
|
(mod-esc-test! "spam triaged" (mod/case-state mod-esc-t1) "triaged")
|
|
(mod-esc-test! "spam triage tier auto" (mod/case-tier mod-esc-t1) "auto")
|
|
(mod-esc-test! "spam triage action hide" (mod/case-action mod-esc-t1) "hide")
|
|
|
|
(define mod-esc-r1 (mod/case-resolve mod-esc-t1))
|
|
(mod-esc-test!
|
|
"auto resolve → decided"
|
|
(mod/case-state mod-esc-r1)
|
|
"decided")
|
|
(mod-esc-test!
|
|
"decision preserved through resolve"
|
|
(mod/case-action mod-esc-r1)
|
|
"hide")
|
|
|
|
;; ── illegal transition flags :error, leaves state ──
|
|
|
|
(define mod-esc-bad (mod/case-finalize mod-esc-c0))
|
|
(mod-esc-test!
|
|
"finalize from open is illegal"
|
|
(mod/case-state mod-esc-bad)
|
|
"open")
|
|
(mod-esc-test!
|
|
"illegal transition sets error"
|
|
(nil? (mod/case-error mod-esc-bad))
|
|
false)
|
|
|
|
;; ── human-tier: repeated report escalates, resolve blocked, review decides ──
|
|
|
|
(define mod-esc-rep-r (mod/mk-report "r3" "ann" "dave" "off-topic"))
|
|
(define mod-esc-rep-reports (list mod-esc-rep-r mod-esc-rep-r mod-esc-rep-r))
|
|
(define mod-esc-rep-c0 (mod/mk-case mod-esc-rep-r))
|
|
(define
|
|
mod-esc-rep-t
|
|
(mod/case-triage mod-esc-rep-c0 mod-esc-rep-reports mod/default-rules))
|
|
|
|
(mod-esc-test!
|
|
"repeated triage action escalate"
|
|
(mod/case-action mod-esc-rep-t)
|
|
"escalate")
|
|
(mod-esc-test!
|
|
"repeated triage tier human"
|
|
(mod/case-tier mod-esc-rep-t)
|
|
"human")
|
|
(mod-esc-test!
|
|
"repeated still triaged after triage"
|
|
(mod/case-state mod-esc-rep-t)
|
|
"triaged")
|
|
|
|
(define mod-esc-rep-block (mod/case-resolve mod-esc-rep-t))
|
|
(mod-esc-test!
|
|
"auto-resolve blocked on human tier (state unchanged)"
|
|
(mod/case-state mod-esc-rep-block)
|
|
"triaged")
|
|
(mod-esc-test!
|
|
"blocked resolve sets error"
|
|
(nil? (mod/case-error mod-esc-rep-block))
|
|
false)
|
|
|
|
(define
|
|
mod-esc-rep-rev
|
|
(mod/case-review
|
|
mod-esc-rep-t
|
|
"confirmed-abuse"
|
|
"human"
|
|
mod-esc-rep-reports
|
|
mod/default-rules))
|
|
(mod-esc-test!
|
|
"human review → decided"
|
|
(mod/case-state mod-esc-rep-rev)
|
|
"decided")
|
|
(mod-esc-test!
|
|
"human review action remove"
|
|
(mod/case-action mod-esc-rep-rev)
|
|
"remove")
|
|
(mod-esc-test!
|
|
"review attached evidence to report"
|
|
(len (mod/report-evidence (mod/case-report mod-esc-rep-rev)))
|
|
1)
|
|
|
|
(define mod-esc-rep-final (mod/case-finalize mod-esc-rep-rev))
|
|
(mod-esc-test!
|
|
"review case finalizes"
|
|
(mod/case-state mod-esc-rep-final)
|
|
"final")
|
|
|
|
;; ── appeal overrides a prior decision ──
|
|
|
|
(define
|
|
mod-esc-ap-c0
|
|
(mod/mk-case (mod/mk-report "r5" "u" "v" "buy now spam")))
|
|
(define mod-esc-ap-rep (list (mod/mk-report "r5" "u" "v" "buy now spam")))
|
|
(define
|
|
mod-esc-ap-t
|
|
(mod/case-triage mod-esc-ap-c0 mod-esc-ap-rep mod/default-rules))
|
|
(define mod-esc-ap-d (mod/case-resolve mod-esc-ap-t))
|
|
|
|
(mod-esc-test!
|
|
"appeal precondition decided/hide"
|
|
(mod/case-action mod-esc-ap-d)
|
|
"hide")
|
|
|
|
(define
|
|
mod-esc-ap-appealed
|
|
(mod/case-appeal
|
|
mod-esc-ap-d
|
|
"exonerated"
|
|
"moderator"
|
|
mod-esc-ap-rep
|
|
mod/default-rules))
|
|
(mod-esc-test!
|
|
"appeal → appealed state"
|
|
(mod/case-state mod-esc-ap-appealed)
|
|
"appealed")
|
|
(mod-esc-test!
|
|
"appeal overrides hide → keep"
|
|
(mod/case-action mod-esc-ap-appealed)
|
|
"keep")
|
|
(mod-esc-test!
|
|
"appeal recorded via exonerated-keep rule"
|
|
(get (mod/case-decision mod-esc-ap-appealed) :rule)
|
|
"exonerated-keep")
|
|
|
|
(define mod-esc-ap-final (mod/case-finalize mod-esc-ap-appealed))
|
|
(mod-esc-test! "appealed → final" (mod/case-state mod-esc-ap-final) "final")
|
|
|
|
;; ── history records the full traversal ──
|
|
|
|
(mod-esc-test!
|
|
"full lifecycle history length 4 (triage,resolve,appeal,finalize)"
|
|
(len (mod/case-history mod-esc-ap-final))
|
|
4)
|
|
(mod-esc-test!
|
|
"first history step open→triaged"
|
|
(get (first (mod/case-history mod-esc-ap-final)) :to)
|
|
"triaged")
|
|
(mod-esc-test!
|
|
"last history step → final"
|
|
(get (nth (mod/case-history mod-esc-ap-final) 3) :to)
|
|
"final")
|
|
|
|
;; ── api-level lifecycle façade ──
|
|
|
|
(mod/reset!)
|
|
(mod/report "alice" "bob" "this is spam")
|
|
(mod/report "carol" "dave" "off-topic")
|
|
(mod/report "carol" "dave" "off-topic")
|
|
(mod/report "carol" "dave" "off-topic")
|
|
|
|
(mod-esc-test!
|
|
"api: case opens at open"
|
|
(mod/case-state (mod/case-of "r1"))
|
|
"open")
|
|
|
|
(define mod-esc-api-t1 (mod/triage "r1"))
|
|
(mod-esc-test!
|
|
"api: triage spam → triaged"
|
|
(mod/case-state mod-esc-api-t1)
|
|
"triaged")
|
|
(mod-esc-test!
|
|
"api: triage spam action hide"
|
|
(mod/case-action mod-esc-api-t1)
|
|
"hide")
|
|
|
|
(define mod-esc-api-r1 (mod/resolve "r1"))
|
|
(mod-esc-test!
|
|
"api: resolve → decided"
|
|
(mod/case-state mod-esc-api-r1)
|
|
"decided")
|
|
(mod-esc-test!
|
|
"api: resolve logged decision"
|
|
(len (mod/audit "r1"))
|
|
1)
|
|
|
|
(define mod-esc-api-app (mod/appeal "r1" "exonerated" "mod"))
|
|
(mod-esc-test!
|
|
"api: appeal → appealed"
|
|
(mod/case-state mod-esc-api-app)
|
|
"appealed")
|
|
(mod-esc-test!
|
|
"api: appeal overrides → keep"
|
|
(mod/case-action mod-esc-api-app)
|
|
"keep")
|
|
(mod-esc-test!
|
|
"api: appeal logged second decision"
|
|
(len (mod/audit "r1"))
|
|
2)
|
|
(mod-esc-test!
|
|
"api: finalize → final"
|
|
(mod/case-state (mod/finalize "r1"))
|
|
"final")
|
|
|
|
;; r4 is the 3rd report about dave → escalates via the human tier
|
|
(define mod-esc-api-t4 (mod/triage "r4"))
|
|
(mod-esc-test!
|
|
"api: repeated triage escalates (human tier)"
|
|
(mod/case-tier mod-esc-api-t4)
|
|
"human")
|
|
(define mod-esc-api-blk (mod/resolve "r4"))
|
|
(mod-esc-test!
|
|
"api: escalated resolve blocked"
|
|
(mod/case-state mod-esc-api-blk)
|
|
"triaged")
|
|
(define mod-esc-api-rev (mod/review "r4" "confirmed-abuse" "human"))
|
|
(mod-esc-test!
|
|
"api: review → decided/remove"
|
|
(mod/case-action mod-esc-api-rev)
|
|
"remove")
|
|
(mod-esc-test! "api: unknown id → nil" (mod/triage "r99") nil)
|
|
|
|
(define mod-escalation-tests-run! (fn () {:failures mod-esc-failures :total mod-esc-count :passed mod-esc-pass :failed mod-esc-fail}))
|