feed: verb-aware smart dedupe — reactions collapse cross-actor, posts stay per-actor + 9 tests
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 54s

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-06 16:54:21 +00:00
parent 58656b03e4
commit 0122c41ecb
6 changed files with 88 additions and 6 deletions

View File

@@ -13,7 +13,7 @@ if [ ! -x "$SX_SERVER" ]; then
exit 1 exit 1
fi fi
SUITES=(basic fanout rank integration content notify home) SUITES=(basic fanout rank integration content notify home dedupe)
OUT_JSON="lib/feed/scoreboard.json" OUT_JSON="lib/feed/scoreboard.json"
OUT_MD="lib/feed/scoreboard.md" OUT_MD="lib/feed/scoreboard.md"

View File

@@ -46,11 +46,31 @@
((a (get ev :activity))) ((a (get ev :activity)))
(list (get ev :to) (get a :actor) (get a :verb) (get a :object))))) (list (get ev :to) (get a :actor) (get a :verb) (get a :object)))))
; verbs whose duplicates collapse across actors (reactions, not authorship).
; rebindable: callers can (set! feed/collapse-verbs ...) to tune the policy.
(define
feed/collapse-verbs
(list "like" "favourite" "follow" "boost" "repost"))
; per-verb key: collapse-verbs fold on (verb object); the rest key on
; (actor verb object).
(define
feed/smart-key
(fn
(a)
(if
(feed/-elem? (get a :verb) feed/collapse-verbs)
(feed/collapse-key a)
(feed/activity-key a))))
; --- ready-made dedupers ---------------------------------------------------- ; --- ready-made dedupers ----------------------------------------------------
(define feed/dedupe-activities (fn (s) (feed/dedupe s feed/activity-key))) (define feed/dedupe-activities (fn (s) (feed/dedupe s feed/activity-key)))
(define feed/dedupe-collapse (fn (s) (feed/dedupe s feed/collapse-key))) (define feed/dedupe-collapse (fn (s) (feed/dedupe s feed/collapse-key)))
; verb-aware: reactions collapse cross-actor, posts stay distinct per actor
(define feed/dedupe-smart (fn (s) (feed/dedupe s feed/smart-key)))
; dedupe an inbox: at most one event per receiver per (actor verb object) ; dedupe an inbox: at most one event per receiver per (actor verb object)
(define feed/dedupe-inbox (fn (inbox) (feed/dedupe inbox feed/event-key))) (define feed/dedupe-inbox (fn (inbox) (feed/dedupe inbox feed/event-key)))

View File

@@ -6,9 +6,10 @@
"integration": {"pass": 22, "fail": 0}, "integration": {"pass": 22, "fail": 0},
"content": {"pass": 15, "fail": 0}, "content": {"pass": 15, "fail": 0},
"notify": {"pass": 8, "fail": 0}, "notify": {"pass": 8, "fail": 0},
"home": {"pass": 6, "fail": 0} "home": {"pass": 6, "fail": 0},
"dedupe": {"pass": 9, "fail": 0}
}, },
"total_pass": 134, "total_pass": 143,
"total_fail": 0, "total_fail": 0,
"total": 134 "total": 143
} }

View File

@@ -11,4 +11,5 @@ _Generated by `lib/feed/conformance.sh`_
| content | 15 | 0 | 15 | | content | 15 | 0 | 15 |
| notify | 8 | 0 | 8 | | notify | 8 | 0 | 8 |
| home | 6 | 0 | 6 | | home | 6 | 0 | 6 |
| **Total** | **134** | **0** | **134** | | dedupe | 9 | 0 | 9 |
| **Total** | **143** | **0** | **143** |

56
lib/feed/tests/dedupe.sx Normal file
View File

@@ -0,0 +1,56 @@
; Follow-up — verb-aware (smart) dedupe. (feed-test name got expected)
; reactions (like/follow) collapse cross-actor; posts stay distinct per actor
(define
M
(feed/stream
(list
(feed/activity "alice" "like" "X" 1 (list))
(feed/activity "bob" "like" "X" 2 (list))
(feed/activity "alice" "post" "P" 3 (list))
(feed/activity "bob" "post" "P" 4 (list))
(feed/activity "alice" "follow" "C" 5 (list))
(feed/activity "bob" "follow" "C" 6 (list))))) ; collapses
(feed-test
"smart dedupe total"
(feed/count (feed/dedupe-smart M))
4)
(feed-test
"smart keeps both posts"
(feed/count (feed/by-verb (feed/dedupe-smart M) "post"))
2)
(feed-test
"smart collapses likes to one"
(feed/count (feed/by-verb (feed/dedupe-smart M) "like"))
1)
(feed-test
"smart collapses follows to one"
(feed/count (feed/by-verb (feed/dedupe-smart M) "follow"))
1)
(feed-test
"collapsed like keeps first actor"
(map feed/actor (feed/items (feed/by-verb (feed/dedupe-smart M) "like")))
(list "alice"))
; contrast: plain activity dedupe keeps cross-actor likes distinct
(feed-test
"activity dedupe keeps both likes"
(feed/count (feed/by-verb (feed/dedupe-activities M) "like"))
2)
; contrast: blanket collapse folds the two posts (same verb+object) too
(feed-test
"collapse dedupe folds posts"
(feed/count (feed/by-verb (feed/dedupe-collapse M) "post"))
1)
; smart-key dispatch
(feed-test
"smart-key reaction -> (verb object)"
(feed/smart-key (feed/activity "alice" "like" "X" 0 (list)))
(list "like" "X"))
(feed-test
"smart-key post -> (actor verb object)"
(feed/smart-key (feed/activity "alice" "post" "P" 0 (list)))
(list "alice" "post" "P"))

View File

@@ -14,7 +14,7 @@ APL, ACL visibility filtering via `lib/acl/`, federation via fed-sx.
## Status (rolling) ## Status (rolling)
`bash lib/feed/conformance.sh`**134/134** (Phases 14 + TF-IDF + notifications + home) `bash lib/feed/conformance.sh`**143/143** (Phases 14 + TF-IDF + notifications + home + smart-dedupe)
## Ground rules ## Ground rules
@@ -147,6 +147,10 @@ are function parameters. Real acl-sx / fed-sx wire in at the call site unchanged
- [x] **Capstone** `feed/home` — the whole pipeline as one line: fanout ∘ inbox ∘ - [x] **Capstone** `feed/home` — the whole pipeline as one line: fanout ∘ inbox ∘
dedupe ∘ ACL ∘ rank ∘ take (`home.sx`); 6 tests incl. per-viewer ACL + cross-post dedupe ∘ ACL ∘ rank ∘ take (`home.sx`); 6 tests incl. per-viewer ACL + cross-post
dedupe. (134/134 total.) dedupe. (134/134 total.)
- [x] Per-verb dedupe rules (briefing gotcha #3) — `feed/dedupe-smart` /
`feed/smart-key`: reactions (like/follow/boost/...) collapse cross-actor on
(verb,object); posts stay distinct per actor. `feed/collapse-verbs` is
rebindable policy; 9 tests. (143/143 total.)
(none) (none)