@@ -488,23 +488,26 @@ headOf = \items -> fold (\acc x -> case acc of
488488 Nothing {} -> Just {value: x}
489489 _ -> acc) Nothing {} items
490490
491−minBy = \items getKey -> fold (\acc x -> case acc of
492− Nothing {} -> Just {value: x}
493− Just {value: v} -> if getKey x < getKey v then Just {value: x} else acc) Nothing {} items
494−
495−maxBy = \items getKey -> fold (\acc x -> case acc of
496− Nothing {} -> Just {value: x}
497− Just {value: v} -> if getKey x > getKey v then Just {value: x} else acc) Nothing {} items
491+-- Sort then take head: O(n log n) but no fold-with-Maybe boilerplate.
492+minBy = \items getKey -> headOf (sortBy getKey items)
493+maxBy = \items getKey -> headOf (sortBy (\x -> 0 - getKey x) items)
498494
499495-- Drop entries with the oldest key (LRU-style eviction; ties drop together)
500496evictOldest = \items getKey ->
501497 case minBy items getKey of
502498 Nothing {} -> items
503− Just {value: oldest} -> do x <- items; where getKey x != getKey oldest; yield x
499+ Just {value: oldest} -> filter items (\x -> getKey x != getKey oldest)
504500
505501capList = \items maxN getKey ->
506502 if count items > maxN then evictOldest items getKey else items
507503
504+-- Shared empty/error shape for /poll. Only a few fields ever vary.
505+emptyPollResp = \cursor authorized rateLimited draining gen retryAfterMs cursorWasReset ->
506+ {events: [], cursor: cursor, authorized: authorized, rateLimited: rateLimited, draining: draining, generation: gen, presenceUpdates: [], heartbeat: False {}, retryAfterMs: retryAfterMs, cursorReset: cursorWasReset, hasMore: False {}, cursorStale: False {}, messageExpirations: [], messageDeliveries: [], droppedDeliveryReceipts: 0, droppedExpirationNotifs: 0, loadPct: 0, messageCancellations: []}
507+
508+-- Shared error shape for /messages send.
509+sendErrResp = \status err -> {status: status, messageId: "", retryAfterMs: 0, error: err, loadPct: 0}
510+
508511-- Sequence an IO action over each unique item (dedup by getKey).
509512forEach = \items getKey action -> case headOf items of
510513 Nothing {} -> yield {}
@@ -1111,12 +1114,12 @@ handlePoll = \cursor generation watchKeys authorization clientIp respond -> do
11111114 let cursorWasReset = generationMismatch && cursor > 0
11121115 let cappedWatchKeys = takeRelation maxWatchKeys watchKeys
11131116 dp <- *drainPhase
1114− if dp >= 2 then yield (respond {events: [], cursor: 0, authorized: True {}, rateLimited: False {}, draining: True {}, generation: serverGen, presenceUpdates: [], heartbeat: False {}, retryAfterMs: 0, cursorReset: cursorWasReset, hasMore: False {}, cursorStale: False {}, messageExpirations: [], messageDeliveries: [], droppedDeliveryReceipts: 0, droppedExpirationNotifs: 0, loadPct: 0, messageCancellations: []})
1117+ if dp >= 2 then yield (respond (emptyPollResp 0 (True {}) (False {}) (True {}) serverGen 0 cursorWasReset))
11151118 else do
11161119 g <- checkGlobalRate t
1117− if g then yield (respond {events: [], cursor: 0, authorized: False {}, rateLimited: True {}, draining: False {}, generation: serverGen, presenceUpdates: [], heartbeat: False {}, retryAfterMs: globalRateWindowMs, cursorReset: cursorWasReset, hasMore: False {}, cursorStale: False {}, messageExpirations: [], messageDeliveries: [], droppedDeliveryReceipts: 0, droppedExpirationNotifs: 0, loadPct: 0, messageCancellations: []})
1120+ if g then yield (respond (emptyPollResp 0 (False {}) (True {}) (False {}) serverGen globalRateWindowMs cursorWasReset))
11181121 else case extractToken authorization of
1119− Nothing {} -> yield (respond {events: [], cursor: 0, authorized: False {}, rateLimited: False {}, draining: False {}, generation: serverGen, presenceUpdates: [], heartbeat: False {}, retryAfterMs: 0, cursorReset: cursorWasReset, hasMore: False {}, cursorStale: False {}, messageExpirations: [], messageDeliveries: [], droppedDeliveryReceipts: 0, droppedExpirationNotifs: 0, loadPct: 0, messageCancellations: []})
1122+ Nothing {} -> yield (respond (emptyPollResp 0 (False {}) (False {}) (False {}) serverGen 0 cursorWasReset))
11201123 Just {value: token} -> pollOnce token effectiveCursor cursorWasReset cappedWatchKeys clientIp serverGen respond t
11211124
11221125pollOnce = \token cursor cursorWasReset watchKeys clientIp serverGen respond t -> do
@@ -1132,7 +1135,7 @@ pollOnce = \token cursor cursorWasReset watchKeys clientIp serverGen respond t -
11321135 startWakeup <- *pollWakeup
11331136 yield (Just {value: {pubkey: pubkey, clampedCursor: clampedCursor, startWakeup: startWakeup, pendingAcks: ackResult.pendingAcks}})
11341137 case setup of
1135− Nothing {} -> yield (respond {events: [], cursor: 0, authorized: False {}, rateLimited: False {}, draining: False {}, generation: serverGen, presenceUpdates: [], heartbeat: False {}, retryAfterMs: 0, cursorReset: cursorWasReset, hasMore: False {}, cursorStale: False {}, messageExpirations: [], messageDeliveries: [], droppedDeliveryReceipts: 0, droppedExpirationNotifs: 0, loadPct: 0, messageCancellations: []})
1138+ Nothing {} -> yield (respond (emptyPollResp 0 (False {}) (False {}) (False {}) serverGen 0 cursorWasReset))
11361139 Just {value: s} -> do
11371140 acquired <- atomic do
11381141 aps <- *activePolls
@@ -1141,7 +1144,7 @@ pollOnce = \token cursor cursorWasReset watchKeys clientIp serverGen respond t -
11411144 appendCapped activePolls [{pubkey: s.pubkey, startedAt: t}] maxActivePolls (\p -> p.startedAt)
11421145 yield True
11431146 if not acquired
1144− then yield (respond {events: [], cursor: cursor, authorized: True {}, rateLimited: True {}, draining: False {}, generation: serverGen, presenceUpdates: [], heartbeat: False {}, retryAfterMs: pollTimeoutMs, cursorReset: cursorWasReset, hasMore: False {}, cursorStale: False {}, messageExpirations: [], messageDeliveries: [], droppedDeliveryReceipts: 0, droppedExpirationNotifs: 0, loadPct: 0, messageCancellations: []})
1147+ then yield (respond (emptyPollResp cursor (True {}) (True {}) (False {}) serverGen pollTimeoutMs cursorWasReset))
11451148 else do
11461149 pollResult <- doPoll s t
11471150 atomic (pruneRef activePolls (\p -> p.pubkey != s.pubkey))
@@ -1209,14 +1212,14 @@ handleSendMessage = \to encryptedBlob timestamp messageId expiresInMs priority a
12091212 let clampedPriority = clamp 0 maxMessagePriority priority
12101213 t <- now
12111214 dp <- *drainPhase
1212− if dp >= 2 then yield (respond {status: MsDraining {}, messageId: "", retryAfterMs: 0, error: "draining", loadPct: 0})
1215+ if dp >= 2 then yield (respond (sendErrResp (MsDraining {}) "draining"))
12131216 else do
12141217 g <- checkGlobalRate t
1215− if g then yield (respond {status: MsRateLimited {}, messageId: "", retryAfterMs: 0, error: "rate_limited", loadPct: 0})
1218+ if g then yield (respond (sendErrResp (MsRateLimited {}) "rate_limited"))
12161219 else do
12171220 effMsgId <- if messageId != "" then yield messageId else genToken
12181221 if t - timestamp > messageMaxAge
1219− then yield (respond {status: MsRejected {}, messageId: "", retryAfterMs: 0, error: "stale_timestamp", loadPct: 0})
1222+ then yield (respond (sendErrResp (MsRejected {}) "stale_timestamp"))
12201223 else do
12211224 let clampedTs = clampTimestamp timestamp t
12221225 result <- atomic do