Side A fixes an actual UI/logic bug in sibling navigation grouping (each unranked sibling now gets its own group instead of being lumped together), with an updated doc comment and a targeted regression test verifying the new behavior. Side B only adds a randomized statistical test for existing rank centrality logic without changing any production code, providing validation but no functional improvement or bugfix.
constitution · epochs · watch · epoch 3
c_3403e5929da7 (tommy-mor) vs c_b7cc67300477 (tommy-mor)
download prompt · raw event · cmp_f89a5f928a21ae
council reasoning
A changes production behavior: unranked siblings become separate nav groups (one link each) instead of one shared group, with an updated doc comment and a focused regression test. B only adds a demo property test for existing rank-centrality behavior and touches no production code, so it adds less lasting product value.
Side A changes production behavior by grouping each unranked sibling into its own navigation group instead of combining all unranked siblings, aligning the implementation with the documented design, and adds a regression test verifying the new grouping. Side B adds only a randomized test for the ranking algorithm without changing functionality, so while it improves validation, it contributes less lasting project behavior.
sides
A — c_3403e5929da7 (tommy-mor)
message
[65bce99f] sibling groups #130
diff preview
diff --git a/server/src/html/garden.rs b/server/src/html/garden.rs
index 82c1b4b4f36ea13a906035a1789ba893222b3695..e3c56d124ab7b0fa08c7f9ae736103e6b7cf5862 100644
--- a/server/src/html/garden.rs
+++ b/server/src/html/garden.rs
@@ -837,7 +837,8 @@ struct SiblingNavGroup {
links: Vec<SiblingNavLink>,
}
-/// Siblings under the same parent, grouped like child rankings (components then isolates).
+/// Siblings under the same parent: one group per ranking component (ordered list), then one
+/// group per isolated unranked sibling (each shows rank `1`, separated like components).
#[derive(Debug, Clone)]
struct SiblingNavBar {
groups: Vec<SiblingNavGroup>,
@@ -890,15 +891,12 @@ fn build_sibling_nav(
groups.push(SiblingNavGroup { links });
}
}
- if !rankings.unranked_items.is_empty() {
- let links: Vec<SiblingNavLink> = rankings
- .unranked_items
- .iter()
- .map(|u| SiblingNavLink {
+ for u in &rankings.unranked_items {
+ groups.push(SiblingNavGroup {
+ links: vec![SiblingNavLink {
path: u.clone().normalized_storage().to_storage_string(),
- })
- .collect();
- groups.push(SiblingNavGroup { links });
+ }],
+ });
}
let sibling_total: usize = groups.iter().map(|g| g.links.len()).sum();
if sibling_total <= 1 {
@@ -1541,6 +1539,29 @@ mod tests {
assert_eq!(nav.groups[1].links.len(), 1);
}
+ #[test]
+ fn sibling_nav_splits_each_unranked_into_its_own_group() {
+ let mut reduced = ReducerState::default();
+ apply_ingest(
+ &mut reduced,
+ 1,
+ "@00000000-0000-0000-0000-000000000000:test:local/test\n\
+ ~/topic {topic body}\n\
+ ~/topic/a {alpha}\n\
+ ~/topic/b {beta}\n\
+ ~/topic/c {gamma}\n\
+ ~/topic/d {delta}\n\
+ {a beats b}\n ~/topic/a 2:1 ~/topic/b\n",
+ );
+
+ let model = build_item_page_view_model(&reduced, &ScopeId::Public, "~/topic/a");
+ let nav = model.sibling_nav.expect("expected sibling nav");
+ assert_eq!(nav.groups.len(), 3);
+ assert_eq!(nav.groups[0].links.len(), 2);
+ assert_eq!(nav.groups[1].links.len(), 1);
+ assert_eq!(nav.groups[2].links.len(), 1);
+ }
+
#[test]
fn item_page_model_builds_ranked_child_components() {
let mut reduced = ReducerState::default();
B — 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();
Hardlinks — judgments / attempts / prompt
judgments
attempts
Prompt text is loaded only by the download route.