B removes genuinely dead/redundant code and correctly updates the test to reflect actual reducer behavior, providing a small but real simplification and behavioral clarification. A merely adds a randomized (non-seeded) test that risks flakiness and mainly demonstrates existing functionality rather than fixing or clarifying behavior.
constitution · epochs · watch · epoch 3
comparison
c_b7cc67300477 (tommy-mor) vs c_cf0a514b261a (tommy-mor)
download prompt · raw event · cmp_dcf82721e591d0
council reasoning
A adds a non-trivial property test that the core rank-centrality path recovers true order from a minimal spanning-tree of perfect-ratio votes, locking in lasting algorithmic behavior. B only deletes a dead zero-ratio early-return and retunes an existing test to the already-enforced contract, which is useful hygiene but much smaller enduring impact.
sides
A — c_b7cc67300477 (tommy-mor)
message
[9e3d989b] Add test that 25 spanning-tree votes with perfect ratios sort the alphabet. Demonstrates rank centrality recovers the true order from a random connected comparison graph when each vote encodes item strength as (idx+1). Co-authored-by: Cursor <cursoragent@cursor.com>
diff preview
diff --git a/server/src/ranking.rs b/server/src/ranking.rs
index c9b4d5d89d2f22cf53a5b43eb979e19a11bc89a1..93cb4c9f5887a1e598cdc9d648751055f618adcc 100644
--- a/server/src/ranking.rs
+++ b/server/src/ranking.rs
@@ -369,6 +369,48 @@ mod tests {
assert_eq!(comp1, vec!["c", "d"]);
}
+ /// A random spanning tree over 26 items needs only n−1 = 25 pairwise votes.
+ /// When each vote uses the "perfect" ratio (strength left : strength right =
+ /// (idx_left+1) : (idx_right+1)), rank centrality recovers the true order.
+ /// See `rank-eric.py` (Eric's demo of Negahban–Oh–Shah rank centrality).
+ #[test]
+ fn twenty_five_random_votes_perfect_ratios_sort_alphabet() {
+ use rand::seq::SliceRandom;
+
+ const N: usize = 26;
+ let letters: Vec<char> = (0..N).map(|i| char::from(b'a' + i as u8)).collect();
+
+ let mut rng = rand::thread_rng();
+ let mut perm: Vec<usize> = (0..N).collect();
+ perm.shuffle(&mut rng);
+
+ let mut g = mk_group();
+ for k in 1..N {
+ let i = *perm[..k].choose(&mut rng).unwrap();
+ let j = perm[k];
+ let (a, b) = (letters[i], letters[j]);
+ g.apply_vote(vote(
+ k as i64,
+ &a.to_string(),
+ &b.to_string(),
+ (i + 1) as i32,
+ (j + 1) as i32,
+ ));
+ }
+
+ let ranked = ranked_items(&g);
+ assert_eq!(ranked.len(), N);
+ for (rank, item) in ranked.iter().enumerate() {
+ let expected = char::from(b'a' + (N - 1 - rank) as u8);
+ assert_eq!(
+ item.item.as_str(),
+ expected.to_string(),
+ "rank {rank}: expected '{expected}', got '{}'",
+ item.item.as_str()
+ );
+ }
+ }
+
#[test]
fn subset_ranking_ranks_within_component_only() {
let mut g = mk_group();
B — c_cf0a514b261a (tommy-mor)
message
[047b82bd] Remove redundant zero-ratio guard from reducer. Zeros are already rejected at the DSL parser and browser handler; the guard in apply_vote was dead code. The negative clamping stays since add_edge_weight already skips weight-0 edges correctly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
diff preview
diff --git a/server/src/reducer.rs b/server/src/reducer.rs
index 0e36979abe0f051493038ff7e652efc7f7a0ac80..efbf7f67ff24c7a2989102fe62879b2794a27c5f 100644
--- a/server/src/reducer.rs
+++ b/server/src/reducer.rs
@@ -112,11 +112,6 @@ impl GroupState {
if vote.ratio_right < 0 {
vote.ratio_right = 0;
}
- if vote.ratio_left == 0 || vote.ratio_right == 0 {
- // Zero on either side produces no valid edge; drop before registering items or pair.
- return;
- }
-
let a_idx = self.ensure_item(&vote.a);
let b_idx = self.ensure_item(&vote.b);
diff --git a/server/tests/basic.rs b/server/tests/basic.rs
index 08159f4a7f0850fd165817a4a1af4f31ced2ad76..1748769ccf7196b2d81cf556d5368c7e723f6a4e 100644
--- a/server/tests/basic.rs
+++ b/server/tests/basic.rs
@@ -533,7 +533,7 @@ fn dsl_parse_rejects_zero_zero_vote_ratio() {
#[test]
fn reducer_negative_ratio_clamped_to_zero() {
let _state = ReducerState::default();
- // GroupState::apply_vote clamps negatives to 0; when either side is 0 the vote is dropped.
+ // apply_vote clamps negatives to 0; add_edge_weight skips zero-weight edges.
let mut group = GroupState::new();
group.apply_vote(slugsocial_server::reducer::VoteData {
ts: 1,
@@ -546,10 +546,9 @@ fn reducer_negative_ratio_clamped_to_zero() {
delegate: Some("00000000-0000-0000-0000-000000000000:test:local/test".to_string()),
thread_tag: "t".to_string(),
});
- // Nothing registered: zero-clamped vote is dropped before ensure_item.
- assert!(group.idx_to_item.is_empty());
+ // Items and pair are registered; edges are absent because weight 0 is skipped.
+ assert_eq!(group.idx_to_item.len(), 2);
assert!(group.edges.is_empty());
- assert!(group.voted_pairs.is_empty());
}
Hardlinks — judgments / attempts / prompt
judgments
attempts
Prompt text is loaded only by the download route.