Files
rose-ash/lib/mod/tests/escalation.sx
giles f4f34c1d33
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 54s
mod: Phase 3 — lifecycle state machine + escalation + appeal, 106/106
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>
2026-06-06 17:50:05 +00:00

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}))