mod: Ext 19 — end-to-end triage pipeline (capstone), 390/390
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 41s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 41s
mod/triage-pipeline domain r reports actor composes domain-policy decision → explanation → AP activity → wire into one bundle. Integration test runs the whole federated path across 5 modules (decide → wire → peer → trust-gated apply), confirming the module-by-module subsystem composes end to end. +15 tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -26,6 +26,7 @@ PRELOADS=(
|
|||||||
lib/mod/wire.sx
|
lib/mod/wire.sx
|
||||||
lib/mod/activity.sx
|
lib/mod/activity.sx
|
||||||
lib/mod/policies.sx
|
lib/mod/policies.sx
|
||||||
|
lib/mod/pipeline.sx
|
||||||
lib/mod/lifecycle.sx
|
lib/mod/lifecycle.sx
|
||||||
lib/mod/audit.sx
|
lib/mod/audit.sx
|
||||||
lib/mod/api.sx
|
lib/mod/api.sx
|
||||||
@@ -55,4 +56,5 @@ SUITES=(
|
|||||||
"activity:lib/mod/tests/activity.sx:(mod-activity-tests-run!)"
|
"activity:lib/mod/tests/activity.sx:(mod-activity-tests-run!)"
|
||||||
"policies:lib/mod/tests/policies.sx:(mod-policies-tests-run!)"
|
"policies:lib/mod/tests/policies.sx:(mod-policies-tests-run!)"
|
||||||
"defrule:lib/mod/tests/defrule.sx:(mod-defrule-tests-run!)"
|
"defrule:lib/mod/tests/defrule.sx:(mod-defrule-tests-run!)"
|
||||||
|
"pipeline:lib/mod/tests/pipeline.sx:(mod-pipeline-tests-run!)"
|
||||||
)
|
)
|
||||||
|
|||||||
18
lib/mod/pipeline.sx
Normal file
18
lib/mod/pipeline.sx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
;; lib/mod/pipeline.sx — end-to-end triage orchestration.
|
||||||
|
;;
|
||||||
|
;; A single entry point that runs a report through the subsystem and returns the
|
||||||
|
;; full artifact bundle: the decision (under the report's domain policy), a
|
||||||
|
;; human-readable explanation, an ActivityPub-shaped event for the bus, and the
|
||||||
|
;; wire line for federated peers. Composes policies (Ext 17), explain (Ext 3),
|
||||||
|
;; activity (Ext 16) and wire (Ext 14) — the modules are independent, this is just
|
||||||
|
;; the convenience that wires them together for the common "process a report" path.
|
||||||
|
|
||||||
|
(define
|
||||||
|
mod/triage-pipeline
|
||||||
|
(fn
|
||||||
|
(domain r reports actor)
|
||||||
|
(let ((d (mod/decide-in domain r reports))) {:activity (mod/decision->activity d actor) :action (get d :action) :wire (mod/decision->wire d) :rule (get d :rule) :decision d :explanation (mod/explain d)})))
|
||||||
|
|
||||||
|
(define mod/pipeline-action (fn (p) (get p :action)))
|
||||||
|
(define mod/pipeline-activity (fn (p) (get p :activity)))
|
||||||
|
(define mod/pipeline-wire (fn (p) (get p :wire)))
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"lang": "mod",
|
"lang": "mod",
|
||||||
"total_passed": 375,
|
"total_passed": 390,
|
||||||
"total_failed": 0,
|
"total_failed": 0,
|
||||||
"total": 375,
|
"total": 390,
|
||||||
"suites": [
|
"suites": [
|
||||||
{"name":"decide","passed":31,"failed":0,"total":31},
|
{"name":"decide","passed":31,"failed":0,"total":31},
|
||||||
{"name":"audit","passed":29,"failed":0,"total":29},
|
{"name":"audit","passed":29,"failed":0,"total":29},
|
||||||
@@ -23,7 +23,8 @@
|
|||||||
{"name":"disjunction","passed":10,"failed":0,"total":10},
|
{"name":"disjunction","passed":10,"failed":0,"total":10},
|
||||||
{"name":"activity","passed":17,"failed":0,"total":17},
|
{"name":"activity","passed":17,"failed":0,"total":17},
|
||||||
{"name":"policies","passed":14,"failed":0,"total":14},
|
{"name":"policies","passed":14,"failed":0,"total":14},
|
||||||
{"name":"defrule","passed":11,"failed":0,"total":11}
|
{"name":"defrule","passed":11,"failed":0,"total":11},
|
||||||
|
{"name":"pipeline","passed":15,"failed":0,"total":15}
|
||||||
],
|
],
|
||||||
"generated": "2026-06-06T19:36:45+00:00"
|
"generated": "2026-06-06T19:40:03+00:00"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# mod scoreboard
|
# mod scoreboard
|
||||||
|
|
||||||
**375 / 375 passing** (0 failure(s)).
|
**390 / 390 passing** (0 failure(s)).
|
||||||
|
|
||||||
| Suite | Passed | Total | Status |
|
| Suite | Passed | Total | Status |
|
||||||
|-------|--------|-------|--------|
|
|-------|--------|-------|--------|
|
||||||
@@ -24,3 +24,4 @@
|
|||||||
| activity | 17 | 17 | ok |
|
| activity | 17 | 17 | ok |
|
||||||
| policies | 14 | 14 | ok |
|
| policies | 14 | 14 | ok |
|
||||||
| defrule | 11 | 11 | ok |
|
| defrule | 11 | 11 | ok |
|
||||||
|
| pipeline | 15 | 15 | ok |
|
||||||
|
|||||||
112
lib/mod/tests/pipeline.sx
Normal file
112
lib/mod/tests/pipeline.sx
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
;; lib/mod/tests/pipeline.sx — Ext 19: end-to-end triage orchestration.
|
||||||
|
|
||||||
|
(define mod-pp-count 0)
|
||||||
|
(define mod-pp-pass 0)
|
||||||
|
(define mod-pp-fail 0)
|
||||||
|
(define mod-pp-failures (list))
|
||||||
|
|
||||||
|
(define
|
||||||
|
mod-pp-test!
|
||||||
|
(fn
|
||||||
|
(name got expected)
|
||||||
|
(begin
|
||||||
|
(set! mod-pp-count (+ mod-pp-count 1))
|
||||||
|
(if
|
||||||
|
(= got expected)
|
||||||
|
(set! mod-pp-pass (+ mod-pp-pass 1))
|
||||||
|
(begin
|
||||||
|
(set! mod-pp-fail (+ mod-pp-fail 1))
|
||||||
|
(append!
|
||||||
|
mod-pp-failures
|
||||||
|
(str name "\n expected: " expected "\n got: " got)))))))
|
||||||
|
|
||||||
|
(mod/policies-reset!)
|
||||||
|
(mod/register-policy!
|
||||||
|
"market"
|
||||||
|
(mod/ruleset
|
||||||
|
(mod/defrule "market-spam-remove" :remove (list :classification "spam"))
|
||||||
|
(mod/defrule "default-keep" :keep)))
|
||||||
|
|
||||||
|
;; ── spam in the market domain: full bundle ──
|
||||||
|
|
||||||
|
(define mod-pp-spam (mod/mk-report "r1" "u" "bob" "this is spam"))
|
||||||
|
(define
|
||||||
|
mod-pp
|
||||||
|
(mod/triage-pipeline "market" mod-pp-spam (list mod-pp-spam) "inst.example"))
|
||||||
|
|
||||||
|
(mod-pp-test!
|
||||||
|
"pipeline action (market policy → remove)"
|
||||||
|
(mod/pipeline-action mod-pp)
|
||||||
|
"remove")
|
||||||
|
(mod-pp-test! "pipeline rule" (get mod-pp :rule) "market-spam-remove")
|
||||||
|
(mod-pp-test!
|
||||||
|
"pipeline explanation mentions the action"
|
||||||
|
(mod/str-contains? (get mod-pp :explanation) "remove")
|
||||||
|
true)
|
||||||
|
(mod-pp-test!
|
||||||
|
"pipeline activity is Delete (remove)"
|
||||||
|
(get (mod/pipeline-activity mod-pp) :type)
|
||||||
|
"Delete")
|
||||||
|
(mod-pp-test!
|
||||||
|
"pipeline activity object is the report"
|
||||||
|
(get (mod/pipeline-activity mod-pp) :object)
|
||||||
|
"r1")
|
||||||
|
(mod-pp-test!
|
||||||
|
"pipeline wire round-trips to the same action"
|
||||||
|
(get (mod/wire->decision (mod/pipeline-wire mod-pp)) :action)
|
||||||
|
"remove")
|
||||||
|
|
||||||
|
;; ── same report, blog domain (default) → hide, Flag ──
|
||||||
|
|
||||||
|
(define
|
||||||
|
mod-pp-blog
|
||||||
|
(mod/triage-pipeline "blog" mod-pp-spam (list mod-pp-spam) "inst.example"))
|
||||||
|
(mod-pp-test!
|
||||||
|
"blog default policy → hide"
|
||||||
|
(mod/pipeline-action mod-pp-blog)
|
||||||
|
"hide")
|
||||||
|
(mod-pp-test!
|
||||||
|
"blog activity is Flag"
|
||||||
|
(get (mod/pipeline-activity mod-pp-blog) :type)
|
||||||
|
"Flag")
|
||||||
|
|
||||||
|
;; ── clean report: keep, no activity, explanation says (none) ──
|
||||||
|
|
||||||
|
(define mod-pp-clean (mod/mk-report "r2" "u" "eve" "a fine post"))
|
||||||
|
(define
|
||||||
|
mod-pp-k
|
||||||
|
(mod/triage-pipeline
|
||||||
|
"market"
|
||||||
|
mod-pp-clean
|
||||||
|
(list mod-pp-clean)
|
||||||
|
"inst.example"))
|
||||||
|
(mod-pp-test! "clean → keep" (mod/pipeline-action mod-pp-k) "keep")
|
||||||
|
(mod-pp-test! "keep → no activity" (mod/pipeline-activity mod-pp-k) nil)
|
||||||
|
(mod-pp-test!
|
||||||
|
"keep explanation says no evidence"
|
||||||
|
(mod/str-contains? (get mod-pp-k :explanation) "Evidence: (none)")
|
||||||
|
true)
|
||||||
|
(mod-pp-test!
|
||||||
|
"keep wire still round-trips"
|
||||||
|
(get (mod/wire->decision (mod/pipeline-wire mod-pp-k)) :rule)
|
||||||
|
"default-keep")
|
||||||
|
|
||||||
|
;; ── federated handoff: market decision crosses to a peer, trust-gated ──
|
||||||
|
|
||||||
|
(mod/fed-reset!)
|
||||||
|
(define mod-pp-peer-dec (mod/wire->decision (mod/pipeline-wire mod-pp)))
|
||||||
|
(mod-pp-test!
|
||||||
|
"untrusted peer: market decision is advisory"
|
||||||
|
(get (mod/fed-receive-decision "peerX" mod-pp-peer-dec) :applied)
|
||||||
|
false)
|
||||||
|
(mod/grant-trust "peerY" :mod)
|
||||||
|
(mod-pp-test!
|
||||||
|
"trusted peer: market decision applies"
|
||||||
|
(get (mod/fed-receive-decision "peerY" mod-pp-peer-dec) :applied)
|
||||||
|
true)
|
||||||
|
(mod-pp-test!
|
||||||
|
"applied action is remove"
|
||||||
|
(get (mod/fed-applied-action "r1") :action)
|
||||||
|
"remove")
|
||||||
|
|
||||||
|
(define mod-pipeline-tests-run! (fn () {:failures mod-pp-failures :total mod-pp-count :passed mod-pp-pass :failed mod-pp-fail}))
|
||||||
@@ -16,7 +16,7 @@ federation extension.
|
|||||||
|
|
||||||
## Status (rolling)
|
## Status (rolling)
|
||||||
|
|
||||||
`bash lib/mod/conformance.sh` → **375/375** (roadmap + 18 extensions complete)
|
`bash lib/mod/conformance.sh` → **390/390** (roadmap + 19 extensions complete)
|
||||||
|
|
||||||
## Ground rules
|
## Ground rules
|
||||||
|
|
||||||
@@ -147,6 +147,12 @@ lib/mod/fed.sx
|
|||||||
derivation goal-by-goal with `[proved]`/`[unproved]` marks and unification
|
derivation goal-by-goal with `[proved]`/`[unproved]` marks and unification
|
||||||
bindings. E.g. `Report rc: escalate (rule: repeated-escalate)` … `[proved]
|
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}`.
|
report(rc, B, S), report_count(S, N), N >= 3 {B=ann, N=3, S=dave}`.
|
||||||
|
- [x] **Ext 19 — end-to-end triage pipeline** (`lib/mod/pipeline.sx`, +15).
|
||||||
|
`mod/triage-pipeline domain r reports actor` runs a report through domain-policy
|
||||||
|
decision → explanation → AP activity → wire, returning the full bundle. The test
|
||||||
|
is a genuine integration across 5 modules including a federated handoff (market
|
||||||
|
decision → wire → peer → trust-gated apply). The capstone that proves the
|
||||||
|
independently-built modules compose.
|
||||||
- [x] **Ext 18 — ergonomic defrule / ruleset** (`lib/mod/defrule.sx`, +11). The
|
- [x] **Ext 18 — ergonomic defrule / ruleset** (`lib/mod/defrule.sx`, +11). The
|
||||||
roadmap's `(defrule …)` surface, done with `&rest` variadics (no macro needed —
|
roadmap's `(defrule …)` surface, done with `&rest` variadics (no macro needed —
|
||||||
conditions are already plain data): `mod/defrule` collects trailing conditions,
|
conditions are already plain data): `mod/defrule` collects trailing conditions,
|
||||||
@@ -234,6 +240,14 @@ lib/mod/fed.sx
|
|||||||
|
|
||||||
## Progress log
|
## Progress log
|
||||||
|
|
||||||
|
- **Ext 19 — end-to-end triage pipeline, 390/390** (+15). Capstone: one
|
||||||
|
orchestration call composes domain policy + decide + explain + activity + wire,
|
||||||
|
and the integration test runs the whole federated path (decide in a domain →
|
||||||
|
wire → peer → trust-gated apply) across 5 modules. Confirms the subsystem — built
|
||||||
|
module-by-module — actually composes end to end. mod-sx now spans schema → policy
|
||||||
|
DSL (boolean algebra + count/score/reporters/burst) → engine + proofs → audit →
|
||||||
|
lifecycle → SLA → federation (trust/wire/AP) → analytics (trace/whatif/lint/batch)
|
||||||
|
→ domain policies → pipeline, all on the green lib/prolog substrate, 390 tests.
|
||||||
- **Ext 18 — ergonomic defrule / ruleset, 375/375** (+11). Closes the roadmap's
|
- **Ext 18 — ergonomic defrule / ruleset, 375/375** (+11). Closes the roadmap's
|
||||||
original `defrule` surface. `fn` supports `&rest` here, and conditions evaluate
|
original `defrule` surface. `fn` supports `&rest` here, and conditions evaluate
|
||||||
to plain data, so no macro is needed — variadic functions give the ergonomics
|
to plain data, so no macro is needed — variadic functions give the ergonomics
|
||||||
|
|||||||
Reference in New Issue
Block a user