mod: Ext 13 — SLA sweep over pending lifecycle cases, 307/307
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 6m28s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 6m28s
Composes lifecycle (Phase 3) with time (Ext 12): a timed-case pairs a case with its state-entry tick; mod/overdue? flags pending cases (open/triaged/appealed) past a deadline; mod/sla-sweep returns the breached report ids. Terminal states never breach. Pure overlay — lifecycle stays timeless, caller stamps entry. +15 tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -21,6 +21,7 @@ PRELOADS=(
|
||||
lib/mod/whatif.sx
|
||||
lib/mod/batch.sx
|
||||
lib/mod/temporal.sx
|
||||
lib/mod/sla.sx
|
||||
lib/mod/lifecycle.sx
|
||||
lib/mod/audit.sx
|
||||
lib/mod/api.sx
|
||||
@@ -44,4 +45,5 @@ SUITES=(
|
||||
"whatif:lib/mod/tests/whatif.sx:(mod-whatif-tests-run!)"
|
||||
"batch:lib/mod/tests/batch.sx:(mod-batch-tests-run!)"
|
||||
"temporal:lib/mod/tests/temporal.sx:(mod-temporal-tests-run!)"
|
||||
"sla:lib/mod/tests/sla.sx:(mod-sla-tests-run!)"
|
||||
)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"lang": "mod",
|
||||
"total_passed": 292,
|
||||
"total_passed": 307,
|
||||
"total_failed": 0,
|
||||
"total": 292,
|
||||
"total": 307,
|
||||
"suites": [
|
||||
{"name":"decide","passed":31,"failed":0,"total":31},
|
||||
{"name":"audit","passed":29,"failed":0,"total":29},
|
||||
@@ -17,7 +17,8 @@
|
||||
{"name":"trace","passed":15,"failed":0,"total":15},
|
||||
{"name":"whatif","passed":13,"failed":0,"total":13},
|
||||
{"name":"batch","passed":17,"failed":0,"total":17},
|
||||
{"name":"temporal","passed":15,"failed":0,"total":15}
|
||||
{"name":"temporal","passed":15,"failed":0,"total":15},
|
||||
{"name":"sla","passed":15,"failed":0,"total":15}
|
||||
],
|
||||
"generated": "2026-06-06T19:00:19+00:00"
|
||||
"generated": "2026-06-06T19:08:06+00:00"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# mod scoreboard
|
||||
|
||||
**292 / 292 passing** (0 failure(s)).
|
||||
**307 / 307 passing** (0 failure(s)).
|
||||
|
||||
| Suite | Passed | Total | Status |
|
||||
|-------|--------|-------|--------|
|
||||
@@ -18,3 +18,4 @@
|
||||
| whatif | 13 | 13 | ok |
|
||||
| batch | 17 | 17 | ok |
|
||||
| temporal | 15 | 15 | ok |
|
||||
| sla | 15 | 15 | ok |
|
||||
|
||||
47
lib/mod/sla.sx
Normal file
47
lib/mod/sla.sx
Normal file
@@ -0,0 +1,47 @@
|
||||
;; lib/mod/sla.sx — service-level sweep over pending lifecycle cases.
|
||||
;;
|
||||
;; Composes the Phase-3 lifecycle with the Ext-12 time dimension: a case left in a
|
||||
;; pending state (open / triaged / appealed) past a deadline has breached SLA and
|
||||
;; should resurface. A timed-case pairs a case with the tick it entered its
|
||||
;; current state (the caller stamps this — the lifecycle stays timeless and pure).
|
||||
;; Terminal states (decided / final) never breach.
|
||||
|
||||
(define mod/pending-states (list "open" "triaged" "appealed"))
|
||||
(define mod/pending-state? (fn (s) (mod/member? s mod/pending-states)))
|
||||
|
||||
(define mod/mk-timed-case (fn (c entered-at) {:entered-at entered-at :case c}))
|
||||
(define mod/tc-case (fn (tc) (get tc :case)))
|
||||
(define mod/tc-entered-at (fn (tc) (get tc :entered-at)))
|
||||
|
||||
(define
|
||||
mod/overdue?
|
||||
(fn
|
||||
(tc now deadline)
|
||||
(if
|
||||
(mod/pending-state? (mod/case-state (mod/tc-case tc)))
|
||||
(< deadline (- now (mod/tc-entered-at tc)))
|
||||
false)))
|
||||
|
||||
(define
|
||||
mod/sla-sweep
|
||||
(fn
|
||||
(timed-cases now deadline)
|
||||
(reduce
|
||||
(fn
|
||||
(acc tc)
|
||||
(if
|
||||
(mod/overdue? tc now deadline)
|
||||
(append
|
||||
acc
|
||||
(list (mod/report-id (mod/case-report (mod/tc-case tc)))))
|
||||
acc))
|
||||
(list)
|
||||
timed-cases)))
|
||||
|
||||
(define
|
||||
mod/overdue-count
|
||||
(fn
|
||||
(timed-cases now deadline)
|
||||
(len (mod/sla-sweep timed-cases now deadline))))
|
||||
|
||||
(define mod/age (fn (tc now) (- now (mod/tc-entered-at tc))))
|
||||
108
lib/mod/tests/sla.sx
Normal file
108
lib/mod/tests/sla.sx
Normal file
@@ -0,0 +1,108 @@
|
||||
;; lib/mod/tests/sla.sx — Ext 13: SLA sweep over pending lifecycle cases.
|
||||
|
||||
(define mod-sla-count 0)
|
||||
(define mod-sla-pass 0)
|
||||
(define mod-sla-fail 0)
|
||||
(define mod-sla-failures (list))
|
||||
|
||||
(define
|
||||
mod-sla-test!
|
||||
(fn
|
||||
(name got expected)
|
||||
(begin
|
||||
(set! mod-sla-count (+ mod-sla-count 1))
|
||||
(if
|
||||
(= got expected)
|
||||
(set! mod-sla-pass (+ mod-sla-pass 1))
|
||||
(begin
|
||||
(set! mod-sla-fail (+ mod-sla-fail 1))
|
||||
(append!
|
||||
mod-sla-failures
|
||||
(str name "\n expected: " expected "\n got: " got)))))))
|
||||
|
||||
;; ── pending-state? ──
|
||||
|
||||
(mod-sla-test! "open is pending" (mod/pending-state? "open") true)
|
||||
(mod-sla-test! "triaged is pending" (mod/pending-state? "triaged") true)
|
||||
(mod-sla-test! "appealed is pending" (mod/pending-state? "appealed") true)
|
||||
(mod-sla-test! "decided is not pending" (mod/pending-state? "decided") false)
|
||||
(mod-sla-test! "final is not pending" (mod/pending-state? "final") false)
|
||||
|
||||
;; build cases in known states
|
||||
(define mod-sla-spam (mod/mk-report "r1" "u" "bob" "this is spam"))
|
||||
(define mod-sla-spam-reports (list mod-sla-spam))
|
||||
(define
|
||||
mod-sla-triaged
|
||||
(mod/case-triage
|
||||
(mod/mk-case mod-sla-spam)
|
||||
mod-sla-spam-reports
|
||||
mod/default-rules))
|
||||
(define mod-sla-decided (mod/case-resolve mod-sla-triaged))
|
||||
(define mod-sla-open (mod/mk-case (mod/mk-report "r2" "u" "eve" "hello")))
|
||||
|
||||
;; ── overdue? ──
|
||||
|
||||
(define mod-sla-tc-old (mod/mk-timed-case mod-sla-triaged 0))
|
||||
(define mod-sla-tc-fresh (mod/mk-timed-case mod-sla-triaged 90))
|
||||
(define mod-sla-tc-done (mod/mk-timed-case mod-sla-decided 0))
|
||||
|
||||
(mod-sla-test!
|
||||
"old triaged case is overdue"
|
||||
(mod/overdue? mod-sla-tc-old 100 50)
|
||||
true)
|
||||
(mod-sla-test!
|
||||
"fresh triaged case not overdue"
|
||||
(mod/overdue? mod-sla-tc-fresh 100 50)
|
||||
false)
|
||||
(mod-sla-test!
|
||||
"decided case never overdue"
|
||||
(mod/overdue? mod-sla-tc-done 100 50)
|
||||
false)
|
||||
(mod-sla-test!
|
||||
"age computes elapsed ticks"
|
||||
(mod/age mod-sla-tc-old 100)
|
||||
100)
|
||||
(mod-sla-test!
|
||||
"boundary: exactly at deadline not overdue"
|
||||
(mod/overdue?
|
||||
(mod/mk-timed-case mod-sla-triaged 50)
|
||||
100
|
||||
50)
|
||||
false)
|
||||
(mod-sla-test!
|
||||
"boundary: one past deadline overdue"
|
||||
(mod/overdue?
|
||||
(mod/mk-timed-case mod-sla-triaged 49)
|
||||
100
|
||||
50)
|
||||
true)
|
||||
|
||||
;; ── sweep over a mixed queue ──
|
||||
|
||||
(define
|
||||
mod-sla-queue
|
||||
(list
|
||||
(mod/mk-timed-case mod-sla-triaged 0)
|
||||
(mod/mk-timed-case mod-sla-decided 0)
|
||||
(mod/mk-timed-case mod-sla-open 90))) ;; r2, pending, age 10 → not
|
||||
|
||||
(mod-sla-test!
|
||||
"sweep finds only the overdue pending case"
|
||||
(mod/sla-sweep mod-sla-queue 100 50)
|
||||
(list "r1"))
|
||||
(mod-sla-test!
|
||||
"overdue-count agrees"
|
||||
(mod/overdue-count mod-sla-queue 100 50)
|
||||
1)
|
||||
|
||||
;; tighten deadline so the young open case also breaches
|
||||
(mod-sla-test!
|
||||
"tighter deadline catches the open case too"
|
||||
(mod/overdue-count mod-sla-queue 100 5)
|
||||
2)
|
||||
(mod-sla-test!
|
||||
"empty queue → no breaches"
|
||||
(mod/sla-sweep (list) 100 50)
|
||||
(list))
|
||||
|
||||
(define mod-sla-tests-run! (fn () {:failures mod-sla-failures :total mod-sla-count :passed mod-sla-pass :failed mod-sla-fail}))
|
||||
@@ -16,7 +16,7 @@ federation extension.
|
||||
|
||||
## Status (rolling)
|
||||
|
||||
`bash lib/mod/conformance.sh` → **292/292** (roadmap + 12 extensions complete)
|
||||
`bash lib/mod/conformance.sh` → **307/307** (roadmap + 13 extensions complete)
|
||||
|
||||
## Ground rules
|
||||
|
||||
@@ -147,6 +147,11 @@ lib/mod/fed.sx
|
||||
derivation goal-by-goal with `[proved]`/`[unproved]` marks and unification
|
||||
bindings. E.g. `Report rc: escalate (rule: repeated-escalate)` … `[proved]
|
||||
report(rc, B, S), report_count(S, N), N >= 3 {B=ann, N=3, S=dave}`.
|
||||
- [x] **Ext 13 — SLA sweep over pending cases** (`lib/mod/sla.sx`, +15). Composes
|
||||
lifecycle (Phase 3) with time (Ext 12): a timed-case pairs a case with the tick
|
||||
it entered its state; `mod/overdue?` flags pending cases (open/triaged/appealed)
|
||||
past a deadline; `mod/sla-sweep` returns the breached report ids. Terminal states
|
||||
never breach. Pure overlay — lifecycle stays timeless, the caller stamps entry.
|
||||
- [x] **Ext 12 — temporal burst detection** (`lib/mod/temporal.sx`, +15). Reports
|
||||
gain an `:at` tick (deterministic, supplied — never clock-read).
|
||||
`mod/decide-temporal now window` counts reports about the subject within
|
||||
@@ -202,6 +207,11 @@ lib/mod/fed.sx
|
||||
|
||||
## Progress log
|
||||
|
||||
- **Ext 13 — SLA sweep, 307/307** (+15). Two subsystems compose cleanly: lifecycle
|
||||
states + temporal ticks → "which pending cases have sat too long". Kept lifecycle
|
||||
pure by having the SLA layer carry entry-time externally (timed-case wrapper)
|
||||
rather than stamping the case — same separation-of-concerns as keeping the state
|
||||
machine out of Prolog.
|
||||
- **Ext 12 — temporal burst detection, 292/292** (+15). Adds the time dimension:
|
||||
a windowed count distinguishes a burst from slow accumulation, where the plain
|
||||
count rule cannot. Time is a supplied tick (`:at`), keeping everything
|
||||
|
||||
Reference in New Issue
Block a user