constitution · epochs · watch · epoch 3

comparison

c_8337e9dd0f87 (tommy-mor) vs c_a28a6860976d (tommy-mor)

download prompt · raw event · cmp_37768543f36aaf

council reasoning

~anthropic/claude-sonnet-latest · winner B · 65:35 · permalink

B is a substantive algorithmic refactor that replaces an O(n²) sort-based pairing scheme with a simpler linear-scan approach, preserving behavior (tests kept, semantics documented) while improving performance and reducing complex enum/sort-key machinery. A is a small, valid concurrency fix (releasing a read lock earlier to avoid writer starvation) but is narrower in scope and impact compared to B's broader design simplification with performance benefits.

~x-ai/grok-latest · winner B · 2:1 · permalink

B replaces an O(n²) candidate-sort that re-derived rank-centrality keys per pair with linear attach/bridge growth plus a single zip pass after one RC order, which is a lasting core-algorithm and scalability fix. A correctly drops the Tokio read guard before layout so writers are not blocked, but that is a localized locking hygiene fix on one handler versus B’s broader complexity and performance win.

openai/gpt-chat-latest · winner B · 4:1 · permalink

Side B substantially redesigns the pair-selection algorithm by replacing per-candidate priority/key computation with a staged approach: linear graph-growth scans (`suggest_grow_pair`), a single rank-centrality pass with adjacent-rank zipping (`zip_adjacent_pair`), and simplified fallback logic. This removes the previous O(n²)-style ranking work while preserving behavior through targeted helper functions, whereas Side A is a focused concurrency improvement that simply precomputes `room_members_section_markup` and drops an `RwLock` read guard before expensive HTML rendering to reduce lock contention.

sides

A — c_8337e9dd0f87 (tommy-mor)

message

[d91e9431] fix(server): drop reduced read lock before room page layout

Precompute room_members_section_markup under the guard, then release
before layout/html stringify. Tokio RwLock queues new readers behind a
waiting writer; holding the read guard across rendering could stall
navigations when ingests take the write lock.

Made-with: Cursor

diff preview

diff --git a/server/src/html/forum.rs b/server/src/html/forum.rs
index f6e45b05bb92126966e304619f80ef1d45be7a4b..a2d36c0645cd5cdd97da78151a2fd574175e67f8 100644
--- a/server/src/html/forum.rs
+++ b/server/src/html/forum.rs
@@ -965,18 +965,23 @@ pub async fn room_page(
         drop(reduced);
         return (StatusCode::NOT_FOUND, "room not found").into_response();
     };
-    let slug_display = room_slug.as_str();
+    // Precompute markup that needs `&ReducerState`, then release the lock before `layout`.
+    // `tokio::sync::RwLock` queues new readers behind a waiting writer; holding the read
+    // guard across HTML/stringify could stall other navigations when an ingest needs a write lock.
+    let members_markup = room_members_section_markup(&reduced, &room_id, false);
     let forum_cli = format!("npx slugsocial private {room_id} forum list");
     let garden_cli = format!("npx slugsocial private {room_id} garden tree");
     let audit_cli = format!("npx slugsocial private {room_id} audit");
+    drop(reduced);
 
+    let slug_display = room_slug.as_str();
     let page = layout(
         &format!("room {slug_display} — slug.social"),
         "view-thread",
         html! {
             (strip)
             nav class="breadcrumb" { (bc_room(&nav, slug_display, None)) }
-            (room_members_section_markup(&reduced, &room_id, false))
+            (members_markup)
             h3 { "threads" }
             (render_thread_feed(Some(&nav), "room-thread-feed", &rows, now))
             @if show_new {
@@ -992,7 +997,6 @@ pub async fn room_page(
         theme_from_jar(&jar),
         &theme_next_from_uri(&uri),
     );
-    drop(reduced);
     Html(page.into_string()).into_response()
 }
 

download full diff A

B — c_a28a6860976d (tommy-mor)

message

[d982f49a] Simplify pair suggestion to avoid O(n²) rank-centrality work.

Grow the voted graph with linear attach/bridge scans, then zip adjacent
ranks with a single RC pass once the pool is fully connected.

Co-authored-by: Cursor <cursoragent@cursor.com>

diff preview

diff --git a/server/src/pair.rs b/server/src/pair.rs
index c14de4b0502c8b5a17cddf3746077739d56e03e0..0687073821b93f7fef69c453f8f921ed5ddfca64 100644
--- a/server/src/pair.rs
+++ b/server/src/pair.rs
@@ -1,16 +1,10 @@
 //! Pick two children of a parent scope for pairwise voting.
 //!
-//! Pair selection prefers **bridge** votes — comparisons between items in
-//! different connected components of the voted-pairs graph — so the pool
-//! merges into one ranking group before refining within it.
+//! Before the pool is one connected voted component, prefer an unvoted edge from
+//! a never-voted child into an established group (spanning tree growth).
 //!
-//! Among unvoted bridges, prefer merging established voted components, then
-//! attaching a never-voted child to an established component, and only then
-//! comparing two never-voted children (so the voted graph grows as one tree).
-//!
-//! Once every pool child sits in one voted component, refinement **zips** down
-//! the rank-centrality order: prefer 1 vs 2, then 2 vs 3, and so on, skipping
-//! pairs that already have a vote.
+//! Once connected, run rank centrality once and **zip** adjacent ranks (1 vs 2,
+//! 2 vs 3, …), skipping pairs that already have a vote.
 
 use rand::seq::SliceRandom;
 use std::collections::{HashMap, HashSet};
@@ -25,6 +19,10 @@ fn pairs_match(a: &ItemId, b: &ItemId, x: &ItemId, y: &ItemId) -> bool {
     (a == x && b == y) || (a == y && b == x)
 }
 
+fn pair_excluded(a: &ItemId, b: &ItemId, exclude: Option<(&ItemId, &ItemId)>) -> bool {
+    exclude.is_some_and(|(x, y)| pairs_match(a, b, x, y))
+}
+
 fn pair_is_voted(group: &GroupState, a: &ItemId, b: &ItemId) -> bool {
     let Some(&ai) = group.item_to_idx.get(a) else {
         return false;
@@ -36,8 +34,6 @@ fn pair_is_voted(group: &GroupState, a: &ItemId, b: &ItemId) -> bool {
     group.voted_pairs.contains(&(i, j))
 }
 
-/// Voted-pairs layout for pool items: component id per item plus which ids are
-/// multi-node voted components (ranked groups in the UI).
 struct ComponentLayout {
     ids: HashMap<ItemId, usize>,
     established: HashSet<usize>,
@@ -77,7 +73,6 @@ fn component_layout(group: &GroupState, pool: &[ItemId]) -> ComponentLayout {
     ComponentLayout { ids, established }
 }
 
-/// Every pool child shares one multi-node voted component (spanning tree phase done).
 fn pool_fully_connected(layout: &ComponentLayout, pool: &[ItemId]) -> bool {
     if pool.len() < 2 {
         return false;
@@ -99,7 +94,13 @@ fn pool_fully_connected(layout: &ComponentLayout, pool: &[ItemId]) -> bool {
     comp_id.is_some()
 }
 
-/// Pool children that appear in `group`, sorted best rank first.
+fn item_in_established(layout: &ComponentLayout, item: &ItemId) -> bool {
+    layout
+        .ids
+        .get(item)
+        .is_some_and(|id| layout.established.contains(id))
+}
+
 fn ranked_pool_order(group: &GroupState, pool: &[ItemId]) -> Vec<ItemId> {
     let pool_set: HashSet<_> = pool.iter().collect();
     ranked_items(group)
@@ -109,170 +110,126 @@ fn ranked_pool_order(group: &GroupState, pool: &[ItemId]) -> Vec<ItemId> {
         .collect()
 }
 
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
-enum PairPriority {
-    /// Unvoted edge between two components — grows the ranking group.
-    BridgeUnvoted = 0,
-    /// Unvoted edge inside one component — refines order.
-    WithinUnvoted = 1,
-    /// Re-vote across components (rare once merged).
-    BridgeVoted = 2,
-    /// Re-vote within a component.
-    WithinVoted = 3,
-}
-
-/// Tie-break among unvoted bridge pairs.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
-enum BridgeSubPriority {
-    /// Both endpoints lie in established (multi-node) voted components.
-    MergeEstablished = 0,
-    /// One established component member and one never-voted child.
-    AttachIsolate = 1,
-    /// Two never-voted children (separate singleton components).
-    IsolatePair = 2,
-}
-
-/// Tie-break among within-component pairs once the pool is one connected group.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
-struct WithinSubPriority {
-    /// 1 = adjacent ranks (i vs i+1); larger = farther apart in the order.
-    rank_gap: usize,
-    /// min rank index of the two — zip from the top (1 vs 2 before 2 vs 3).
-    zip_index: usize,
-}
-
-const WITHIN_SUB_WORST: WithinSubPriority = WithinSubPriority {
-    rank_gap: usize::MAX,
-    zip_index: usize::MAX,
-};
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
-struct PairSortKey {
-    priority: PairPriority,
-    bridge_sub: BridgeSubPriority,
-    within_sub: WithinSubPriority,
-}
-
-fn item_in_established(layout: &ComponentLayout, item: &ItemId) -> bool {
-    layout
-        .ids
-        .get(item)
-        .is_some_and(|id| layout.established.contains(id))
-}
-
-fn bridge_sub_priority(layout: &ComponentLayout, a: &ItemId, b: &ItemId) -> BridgeSubPriority {
-    let a_est = item_in_established(layout, a);
-    let b_est = item_in_established(layout, b);
-    match (a_est, b_est) {
-        (true, true) => BridgeSubPriority::MergeEstablished,
-        (true, false) | (false, true) => BridgeSubPriority::AttachIsolate,
-        (false, false) => BridgeSubPriority::IsolatePair,
+/// Walk 1↔2, 2↔3, …; optional `require_unvoted` skips voted edges.
+fn zip_adjacent_pair(
+    group: &GroupState,
+    order: &[ItemId],
+    exclude: Option<(&ItemId, &ItemId)>,
+    require_unvoted: bool,
+) -> Option<(ItemId, ItemId)> {
+    for w in order.windows(2) {
+        let a = &w[0];
+        let b = &w[1];
+        if pair_excluded(a, b, exclude) {
+            continue;
+        }
+        if require_unvoted && pair_is_voted(group, a, b) {
+            continue;
+        }
+        return Some((a.clone(), b.clone()));
     }
+    None
 }
 
-fn within_sub_priority(
+/// Grow the voted graph toward one component (no rank centrality).
+fn suggest_grow_pair(
     group: &GroupState,
     pool: &[ItemId],
     layout: &ComponentLayout,
-    a: &ItemId,
-    b: &ItemId,
-) -> WithinSubPriority {
-    if !pool_fully_connected(layout, pool) {
-        return WITHIN_SUB_WORST;
-    }
-    let order = ranked_pool_order(group, pool);
-    let (Some(i), Some(j)) = (order.iter().position(|x| x == a), order.iter().position(|x| x == b))
-    else {
-        return WITHIN_SUB_WORST;
-    };
-    WithinSubPriority {
-        rank_gap: i.abs_diff(j),
-        zip_index: i.min(j),
+    exclude: Option<(&ItemId, &ItemId)>,
+) -> Option<(ItemId, ItemId)> {
+    let established: Vec<&ItemId> = pool
+        .iter()
+        .filter(|item| item_in_established(layout, item))
+        .collect();
+    let isolates: Vec<&ItemId> = pool
+        .iter()
+        .filter(|item| !item_in_established(layout, item))
+        .collect();
+
+    // Attach a never-voted child to the established mass.
+    for iso in &isolates {
+        for est in &established {
+            if !pair_is_voted(group, iso, est) && !pair_excluded(iso, est, exclude) {
+                return Some(((*iso).clone(), (*est).clone()));
+            }
+        }
     }
-}
 
-fn pair_sort_key(
-    group: &GroupState,
-    pool: &[ItemId],
-    layout: &ComponentLayout,
-    a: &ItemId,
-    b: &ItemId,
-) -> PairSortKey {
-    let voted = pair_is_voted(group, a, b);
-    let bridge = layout.ids.get(a) != layout.ids.get(b);
-    let priority = match (bridge, voted) {
-        (true, false) => PairPriority::BridgeUnvoted,
-        (false, false) => PairPriority::WithinUnvoted,
-        (true, true) => PairPriority::BridgeVoted,
-        (false, true) => PairPriority::WithinVoted,
-    };
-    let bridge_sub = if priority == PairPriority::BridgeUnvoted {
-        bridge_sub_priority(layout, a, b)
-    } else {
-        BridgeSubPriority::MergeEstablished
-    };
-    let within_sub = if matches!(
-        priority,
-        PairPriority::WithinUnvoted | PairPriority::WithinVoted
-    ) {
-        within_sub_priority(group, pool, layout, a, b)
-    } else {
-        WITHIN_SUB_WORST
-    };
-    PairSortKey {
-        priority,
-        bridge_sub,
-        within_sub,
+    // Bridge two established components.
+    for i in 0..established.len() {
+        for j in (i + 1)..established.len() {
+            let a = established[i];
+            let b = established[j];
+            if layout.ids.get(a) == layout.ids.get(b) {
+                continue;
+            }
+            if !pair_is_voted(group, a, b) && !pair_excluded(a, b, exclude) {
+                return Some((a.clone(), b.clone()));
+            }
+        }
     }
-}
 
-/// All unordered pairs from `pool`, optionally skipping `exclude`.
-fn candidate_pairs(pool: &[ItemId], exclude: Option<(&ItemId, &ItemId)>) -> Vec<(ItemId, ItemId)> {
-    let mut out = Vec::new();
+    // Any other unvoted pair (e.g. two isolates).
     for i in 0..pool.len() {
         for j in (i + 1)..pool.len() {
             let a = &pool[i];
             let b = &pool[j];
-            if a == b {
-                continue;
-            }
-            if exclude.is_some_and(|(x, y)| pairs_match(a, b, x, y)) {
+            if pair_is_voted(group, a, b) || pair_excluded(a, b, exclude) {
                 continue;
             }
-            out.push((a.clone(), b.clone()));
+            return Some((a.clone(), b.clone()));
         }
     }
-    out
+
+    None
 }
 
 /// Pick the next pair to vote on within `pool`.
-///
-/// 1. Prefer unvoted **bridge** pairs (connect separate ranking components),
-///    with sub-priority: merge established components, attach an isolate to
-///    established, then compare two isolates.
-/// 2. Then unvoted within-component pairs; when the pool is one connected group,
-///    prefer adjacent ranks (1 vs 2, 2 vs 3, …) in order, skipping voted pairs.
-/// 3. Then already-voted pairs (re-compare), with the same zip ordering.
 pub fn suggest_next_pair_in_pool(
     group: &GroupState,
     pool: &[ItemId],
     exclude: Option<(&ItemId, &ItemId)>,
 ) -> Option<(ItemId, ItemId)> {
-    let candidates = candidate_pairs(pool, exclude);
-    if candidates.is_empty() {
+    if pool.len() < 2 {
         return None;
     }
+
     let layout = component_layout(group, pool);
-    let best = candidates
-        .iter()
-        .map(|(a, b)| (pair_sort_key(group, pool, &layout, a, b), (a, b)))
-        .min_by_key(|(k, _)| *k)?
-        .0;
-    let best_pairs: Vec<(ItemId, ItemId)> = candidates
-        .into_iter()
-        .filter(|(a, b)| pair_sort_key(group, pool, &layout, a, b) == best)
-        .collect();
-    best_pairs.choose(&mut rand::thread_rng()).cloned()
+
+    if pool_fully_connected(&layout, pool) {
+        let order = ranked_pool_order(group, pool);
+        if let Some(pair) = zip_adjacent_pair(group, &order, exclude, true) {
+            return Some(pair);
+        }
+        if let Some(pair) = zip_adjacent_pair(group, &order, exclude, false) {
+            return Some(pair);
+        }
+    }
+
+    if let Some(pair) = suggest_grow_pair(group, pool, &layout, exclude) {
+        return Some(pair);
+    }
+
+    // Re-vote: zip order when connected, else any non-excluded pair.
+    if pool_fully_connected(&layout, pool) {
+        let order = ranked_pool_order(group, pool);
+        if let Some(pair) = zip_adjacent_pair(group, &order, exclude, false) {
+            return Some(pair);
+        }
+    }
+
+    for i in 0..pool.len() {
+        for j in (i + 1)..pool.len() {
+            let a = &pool[i];
+            let b = &pool[j];
+            if !pair_excluded(a, b, exclude) {
+                return Some((a.clone(), b.clone()));
+            }
+        }
+    }
+
+    None
 }
 
 /// Random distinct pair from `children` (legacy pair.rs behavior).
@@ -509,7 +466,6 @@ mod tests {
                 "reddit.com/r/rust/c",
             ],
         );
-        // Star at a connects all three; b-c is the only unvoted adjacent pair left.
         for (a, b, l, r) in [
             ("

… preview truncated; 1,439 characters omitted

download full diff B

Hardlinks — judgments / attempts / prompt

prompt download

judgments

attempts

Prompt text is loaded only by the download route.