H3: votes are atomic claims on the persist stream (TDD)
Failing tests first (2 red: the vote wasn't on the stream, and dedup vanished if the projection edge was removed — proving it was an edge-scan). host/blog-vote now acquires on stream 'vote:<poll>' via ev/book! (append-expect, retry-on-conflict — the same atomicity as seats); the option --voted--> edge is a projection recorded only on :booked. Removed the read-check-write host/blog--voted-in-poll?. Governance-grade: no double vote under concurrency, dedup survives projection wipes + restarts (store-backed). blog suite 238/238 (+4). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -3364,10 +3364,6 @@
|
||||
people)))))))))))
|
||||
;; ── POLLS (non-commercial): a post carries polls; a VOTE is a Claim with money + capacity turned
|
||||
;; OFF — the same "acquire, deduped by actor" shape as a booking, one vote per voter per poll.
|
||||
(define host/blog--voted-in-poll?
|
||||
(fn (poll voter)
|
||||
(some (fn (opt) (contains? (host/blog--out-raw opt "voted") voter))
|
||||
(host/blog--out-raw poll "option"))))
|
||||
(define host/blog--poll-view
|
||||
(fn (poll)
|
||||
(let ((q (get (host/blog-field-values-of poll) "question")))
|
||||
@@ -3427,8 +3423,13 @@
|
||||
(let ((poll (dream-query-param req "poll")) (option (dream-query-param req "option"))
|
||||
(voter (or (host/field req "voter") "")))
|
||||
(begin
|
||||
(when (and poll option (not (= voter "")) (not (host/blog--voted-in-poll? poll voter)))
|
||||
(host/blog-relate! option voter "voted"))
|
||||
;; H3: one-vote-per-voter is an ATOMIC claim on the persist stream "vote:<poll>" — the same
|
||||
;; append-expect acquire as seats (ev/book! :already = already voted). The option edge is a
|
||||
;; projection recorded only when the claim lands; the stream is the source of truth.
|
||||
(when (and poll option (not (= voter "")))
|
||||
(let ((claim (ev/book! host/blog-store (str "vote:" poll) 1000000000 voter)))
|
||||
(when (= (get claim :status) :booked)
|
||||
(host/blog-relate! option voter "voted"))))
|
||||
(let ((posts (host/blog-in poll "has-poll")))
|
||||
(dream-redirect (str "/" (if (> (len posts) 0) (first posts) "") "/")))))))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user