mod: Phase 4 — federation (trust, sharing, revocation), 132/132 — roadmap done
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 55s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 55s
Cross-instance reports ingest into the local registry with origin tags; the engine decides them unchanged. Decision sharing pushes to a mock fed-sx outbox (mod/fed-send! is the transport seam). Trust is advisory by default: a peer's decision binds locally only under (mod/trusted? peer :mod), else it lands in the advisory log unapplied. Revocation composes with the Phase-2 proof model — fed-revoke-if-invalidated re-runs the engine and undoes moderation only when the action no longer holds (exoneration flips hide→keep → revoked + origin notified). +26 fed tests. Full mod-on-sx roadmap complete. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -16,10 +16,12 @@ PRELOADS=(
|
||||
lib/mod/lifecycle.sx
|
||||
lib/mod/audit.sx
|
||||
lib/mod/api.sx
|
||||
lib/mod/fed.sx
|
||||
)
|
||||
|
||||
SUITES=(
|
||||
"decide:lib/mod/tests/decide.sx:(mod-decide-tests-run!)"
|
||||
"audit:lib/mod/tests/audit.sx:(mod-audit-tests-run!)"
|
||||
"escalation:lib/mod/tests/escalation.sx:(mod-escalation-tests-run!)"
|
||||
"fed:lib/mod/tests/fed.sx:(mod-fed-tests-run!)"
|
||||
)
|
||||
|
||||
145
lib/mod/fed.sx
Normal file
145
lib/mod/fed.sx
Normal file
@@ -0,0 +1,145 @@
|
||||
;; lib/mod/fed.sx — federation: cross-instance reports, decision sharing, trust,
|
||||
;; revocation. fed-sx itself is mocked here (an in-memory outbox); the real wire
|
||||
;; transport would replace mod/fed-send!.
|
||||
;;
|
||||
;; Trust is advisory by default (the hard rule): a peer's decision only binds
|
||||
;; locally when (mod/trusted? peer :mod) holds. An untrusted peer's decision is
|
||||
;; recorded as a suggestion in the advisory log and is NOT applied. Local
|
||||
;; decisions propagate outward via the outbox. Revocation undoes a locally
|
||||
;; applied action when its proof is invalidated, notifying the origin peer.
|
||||
|
||||
(define mod/*fed-trust* (list)) ;; {:peer :scope}
|
||||
(define mod/*fed-outbox* (list)) ;; {:to :type :payload}
|
||||
(define mod/*fed-advisory* (list)) ;; {:peer :decision} — received, not applied
|
||||
(define mod/*fed-applied* (list)) ;; {:report-id :action :origin :revoked}
|
||||
(define mod/*fed-origins* (list)) ;; {:id :origin}
|
||||
|
||||
(define
|
||||
mod/fed-reset!
|
||||
(fn
|
||||
()
|
||||
(begin
|
||||
(set! mod/*fed-trust* (list))
|
||||
(set! mod/*fed-outbox* (list))
|
||||
(set! mod/*fed-advisory* (list))
|
||||
(set! mod/*fed-applied* (list))
|
||||
(set! mod/*fed-origins* (list)))))
|
||||
|
||||
;; ── trust model ──
|
||||
|
||||
(define
|
||||
mod/trust-match?
|
||||
(fn
|
||||
(t peer scope)
|
||||
(if (= (get t :peer) peer) (= (get t :scope) scope) false)))
|
||||
|
||||
(define
|
||||
mod/grant-trust
|
||||
(fn (peer scope) (begin (append! mod/*fed-trust* {:scope scope :peer peer}) true)))
|
||||
|
||||
(define
|
||||
mod/revoke-trust
|
||||
(fn
|
||||
(peer scope)
|
||||
(set!
|
||||
mod/*fed-trust*
|
||||
(reduce
|
||||
(fn
|
||||
(acc t)
|
||||
(if (mod/trust-match? t peer scope) acc (append acc (list t))))
|
||||
(list)
|
||||
mod/*fed-trust*))))
|
||||
|
||||
(define
|
||||
mod/trusted?
|
||||
(fn
|
||||
(peer scope)
|
||||
(mod/any? (fn (t) (mod/trust-match? t peer scope)) mod/*fed-trust*)))
|
||||
|
||||
;; ── cross-instance reports ──
|
||||
|
||||
(define
|
||||
mod/fed-receive-report
|
||||
(fn
|
||||
(peer by about reason)
|
||||
(let
|
||||
((r (mod/report by about reason)))
|
||||
(begin (append! mod/*fed-origins* {:id (mod/report-id r) :origin peer}) r))))
|
||||
|
||||
(define
|
||||
mod/report-origin
|
||||
(fn
|
||||
(id)
|
||||
(reduce
|
||||
(fn (acc o) (if (= (get o :id) id) (get o :origin) acc))
|
||||
"local"
|
||||
mod/*fed-origins*)))
|
||||
|
||||
;; ── decision sharing (mock fed-sx send) ──
|
||||
|
||||
(define
|
||||
mod/fed-send!
|
||||
(fn (to type payload) (begin (append! mod/*fed-outbox* {:type type :to to :payload payload}) true)))
|
||||
|
||||
(define mod/fed-outbox (fn () mod/*fed-outbox*))
|
||||
|
||||
(define
|
||||
mod/fed-share-decision
|
||||
(fn
|
||||
(decision peers)
|
||||
(reduce
|
||||
(fn
|
||||
(acc p)
|
||||
(begin (mod/fed-send! p "decision" decision) (append acc (list p))))
|
||||
(list)
|
||||
peers)))
|
||||
|
||||
;; ── receiving a peer's decision (advisory unless trusted) ──
|
||||
|
||||
(define
|
||||
mod/fed-applied-action
|
||||
(fn
|
||||
(report-id)
|
||||
(reduce
|
||||
(fn (acc a) (if (= (get a :report-id) report-id) a acc))
|
||||
nil
|
||||
mod/*fed-applied*)))
|
||||
|
||||
(define
|
||||
mod/fed-receive-decision
|
||||
(fn
|
||||
(peer decision)
|
||||
(if
|
||||
(mod/trusted? peer :mod)
|
||||
(begin (append! mod/*fed-applied* {:revoked false :action (get decision :action) :report-id (get decision :report-id) :origin peer}) {:advisory false :peer peer :applied true :decision decision})
|
||||
(begin (append! mod/*fed-advisory* {:peer peer :decision decision}) {:advisory true :peer peer :applied false :decision decision}))))
|
||||
|
||||
;; ── revocation ──
|
||||
|
||||
(define
|
||||
mod/fed-revoke!
|
||||
(fn
|
||||
(report-id reason)
|
||||
(begin
|
||||
(set!
|
||||
mod/*fed-applied*
|
||||
(map
|
||||
(fn (a) (if (= (get a :report-id) report-id) {:revoked true :action (get a :action) :report-id (get a :report-id) :origin (get a :origin)} a))
|
||||
mod/*fed-applied*))
|
||||
(mod/fed-send! (mod/report-origin report-id) "revocation" {:report-id report-id :reason reason})
|
||||
report-id)))
|
||||
|
||||
;; re-run the engine; if the action no longer holds, the prior decision's proof
|
||||
;; is invalidated — revoke the applied moderation.
|
||||
(define
|
||||
mod/fed-revoke-if-invalidated
|
||||
(fn
|
||||
(report decision reports rules)
|
||||
(let
|
||||
((d2 (mod/decide-report report reports rules)))
|
||||
(if
|
||||
(= (get d2 :action) (get decision :action))
|
||||
{:revoked false :decision d2}
|
||||
(begin
|
||||
(mod/fed-revoke! (get decision :report-id) "proof invalidated")
|
||||
{:revoked true :decision d2})))))
|
||||
@@ -1,12 +1,13 @@
|
||||
{
|
||||
"lang": "mod",
|
||||
"total_passed": 106,
|
||||
"total_passed": 132,
|
||||
"total_failed": 0,
|
||||
"total": 106,
|
||||
"total": 132,
|
||||
"suites": [
|
||||
{"name":"decide","passed":31,"failed":0,"total":31},
|
||||
{"name":"audit","passed":29,"failed":0,"total":29},
|
||||
{"name":"escalation","passed":46,"failed":0,"total":46}
|
||||
{"name":"escalation","passed":46,"failed":0,"total":46},
|
||||
{"name":"fed","passed":26,"failed":0,"total":26}
|
||||
],
|
||||
"generated": "2026-06-06T17:49:32+00:00"
|
||||
"generated": "2026-06-06T17:54:02+00:00"
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# mod scoreboard
|
||||
|
||||
**106 / 106 passing** (0 failure(s)).
|
||||
**132 / 132 passing** (0 failure(s)).
|
||||
|
||||
| Suite | Passed | Total | Status |
|
||||
|-------|--------|-------|--------|
|
||||
| decide | 31 | 31 | ok |
|
||||
| audit | 29 | 29 | ok |
|
||||
| escalation | 46 | 46 | ok |
|
||||
| fed | 26 | 26 | ok |
|
||||
|
||||
154
lib/mod/tests/fed.sx
Normal file
154
lib/mod/tests/fed.sx
Normal file
@@ -0,0 +1,154 @@
|
||||
;; lib/mod/tests/fed.sx — Phase 4: federation (mock fed-sx).
|
||||
|
||||
(define mod-fed-count 0)
|
||||
(define mod-fed-pass 0)
|
||||
(define mod-fed-fail 0)
|
||||
(define mod-fed-failures (list))
|
||||
|
||||
(define
|
||||
mod-fed-test!
|
||||
(fn
|
||||
(name got expected)
|
||||
(begin
|
||||
(set! mod-fed-count (+ mod-fed-count 1))
|
||||
(if
|
||||
(= got expected)
|
||||
(set! mod-fed-pass (+ mod-fed-pass 1))
|
||||
(begin
|
||||
(set! mod-fed-fail (+ mod-fed-fail 1))
|
||||
(append!
|
||||
mod-fed-failures
|
||||
(str name "\n expected: " expected "\n got: " got)))))))
|
||||
|
||||
(mod/reset!)
|
||||
(mod/fed-reset!)
|
||||
|
||||
;; ── trust model (advisory by default) ──
|
||||
|
||||
(mod-fed-test! "trust initially false" (mod/trusted? "peerA" :mod) false)
|
||||
(mod/grant-trust "peerA" :mod)
|
||||
(mod-fed-test! "trust after grant" (mod/trusted? "peerA" :mod) true)
|
||||
(mod-fed-test! "trust wrong scope" (mod/trusted? "peerA" :other) false)
|
||||
(mod-fed-test! "trust other peer" (mod/trusted? "peerB" :mod) false)
|
||||
(mod/revoke-trust "peerA" :mod)
|
||||
(mod-fed-test! "trust after revoke" (mod/trusted? "peerA" :mod) false)
|
||||
|
||||
;; ── cross-instance reports ──
|
||||
|
||||
(define
|
||||
mod-fed-fr
|
||||
(mod/fed-receive-report "peerB" "alice" "bob" "this is spam"))
|
||||
(mod-fed-test! "fed report assigned id r1" (mod/report-id mod-fed-fr) "r1")
|
||||
(mod-fed-test! "fed report origin is peer" (mod/report-origin "r1") "peerB")
|
||||
(define mod-fed-local (mod/report "carol" "dave" "fine post"))
|
||||
(mod-fed-test!
|
||||
"local report origin is local"
|
||||
(mod/report-origin (mod/report-id mod-fed-local))
|
||||
"local")
|
||||
(mod-fed-test!
|
||||
"engine decides fed report (spam → hide)"
|
||||
(get
|
||||
(mod/decide-report mod-fed-fr (list mod-fed-fr) mod/default-rules)
|
||||
:action)
|
||||
"hide")
|
||||
|
||||
;; ── decision sharing (outbox) ──
|
||||
|
||||
(define mod-fed-dec {:action "hide" :rule "spam-hide" :report-id "r1"})
|
||||
(define
|
||||
mod-fed-shared
|
||||
(mod/fed-share-decision mod-fed-dec (list "peerB" "peerC")))
|
||||
(mod-fed-test! "share returns notified peers" (len mod-fed-shared) 2)
|
||||
(mod-fed-test! "outbox has two messages" (len (mod/fed-outbox)) 2)
|
||||
(mod-fed-test!
|
||||
"outbox message type decision"
|
||||
(get (first (mod/fed-outbox)) :type)
|
||||
"decision")
|
||||
(mod-fed-test!
|
||||
"outbox message addressed to peer"
|
||||
(get (first (mod/fed-outbox)) :to)
|
||||
"peerB")
|
||||
|
||||
;; ── receiving a peer decision: advisory unless trusted ──
|
||||
|
||||
(define mod-fed-untrusted (mod/fed-receive-decision "peerZ" {:action "remove" :rule "reviewer-remove" :report-id "rx"}))
|
||||
(mod-fed-test!
|
||||
"untrusted decision not applied"
|
||||
(get mod-fed-untrusted :applied)
|
||||
false)
|
||||
(mod-fed-test!
|
||||
"untrusted decision advisory"
|
||||
(get mod-fed-untrusted :advisory)
|
||||
true)
|
||||
(mod-fed-test!
|
||||
"untrusted decision absent from applied log"
|
||||
(mod/fed-applied-action "rx")
|
||||
nil)
|
||||
(mod-fed-test!
|
||||
"advisory log records suggestion"
|
||||
(len mod/*fed-advisory*)
|
||||
1)
|
||||
|
||||
(mod/grant-trust "peerT" :mod)
|
||||
(define mod-fed-trusted (mod/fed-receive-decision "peerT" {:action "hide" :rule "spam-hide" :report-id "ry"}))
|
||||
(mod-fed-test! "trusted decision applied" (get mod-fed-trusted :applied) true)
|
||||
(mod-fed-test!
|
||||
"trusted decision binds locally"
|
||||
(get (mod/fed-applied-action "ry") :action)
|
||||
"hide")
|
||||
|
||||
;; ── revocation ──
|
||||
|
||||
(mod-fed-test!
|
||||
"applied action not yet revoked"
|
||||
(get (mod/fed-applied-action "ry") :revoked)
|
||||
false)
|
||||
(mod/fed-revoke! "ry" "manual")
|
||||
(mod-fed-test!
|
||||
"revoke marks applied action revoked"
|
||||
(get (mod/fed-applied-action "ry") :revoked)
|
||||
true)
|
||||
(mod-fed-test!
|
||||
"revoke emits a revocation message"
|
||||
(mod/any? (fn (m) (= (get m :type) "revocation")) (mod/fed-outbox))
|
||||
true)
|
||||
|
||||
;; revoke-if-invalidated: proof still holds → no revocation
|
||||
(define mod-fed-spam-r (mod/mk-report "rs" "a" "b" "this is spam"))
|
||||
(define
|
||||
mod-fed-spam-d
|
||||
(mod/decide-report mod-fed-spam-r (list mod-fed-spam-r) mod/default-rules))
|
||||
(mod-fed-test! "spam decision is hide" (get mod-fed-spam-d :action) "hide")
|
||||
(define
|
||||
mod-fed-rev-same
|
||||
(mod/fed-revoke-if-invalidated
|
||||
mod-fed-spam-r
|
||||
mod-fed-spam-d
|
||||
(list mod-fed-spam-r)
|
||||
mod/default-rules))
|
||||
(mod-fed-test!
|
||||
"valid proof → not revoked"
|
||||
(get mod-fed-rev-same :revoked)
|
||||
false)
|
||||
|
||||
;; exoneration invalidates the proof → revocation
|
||||
(define
|
||||
mod-fed-exon-r
|
||||
(mod/attach-evidence mod-fed-spam-r (mod/mk-evidence "exonerated" "mod")))
|
||||
(define
|
||||
mod-fed-rev-inv
|
||||
(mod/fed-revoke-if-invalidated
|
||||
mod-fed-exon-r
|
||||
mod-fed-spam-d
|
||||
(list mod-fed-exon-r)
|
||||
mod/default-rules))
|
||||
(mod-fed-test!
|
||||
"invalidated proof → revoked"
|
||||
(get mod-fed-rev-inv :revoked)
|
||||
true)
|
||||
(mod-fed-test!
|
||||
"re-decision after exoneration is keep"
|
||||
(get (get mod-fed-rev-inv :decision) :action)
|
||||
"keep")
|
||||
|
||||
(define mod-fed-tests-run! (fn () {:failures mod-fed-failures :total mod-fed-count :passed mod-fed-pass :failed mod-fed-fail}))
|
||||
Reference in New Issue
Block a user