You are a constitutional council ranking individual git commits for ownership allocation. Compare these two commits. Decide which contributed more lasting value to the project. Judge substance, not spectacle: - Prefer correct, lasting design and real bugfixes over churn, formatting, renames, or generated noise. - Prefer clarity and necessity over sheer line count. A small precise change can beat a large diffuse one. - Do not favor a side merely because its patch is longer or noisier. - Weight what the change does for the project, not the contributor's name. Return ONLY a JSON object: {"winner": "A" or "B", "ratio": "N:M", "explanation": "..."} The explanation must cite concrete differences in the patches (1-3 sentences). Side A — contributor: tommy-mor Side A — commit message: [14749a34] Show graph topology with pair suggestions Expose existing connectivity statistics in CLI output so voters can see sparse or disconnected scopes before adding an edge. Co-authored-by: Cursor Side A — unified diff (full patch): diff --git a/cli/src/main.rs b/cli/src/main.rs index 70435b412188a151c5e89e842a5de57f7480ddf2..abb5a55b49f60fe28fbfd4ec02715cb94ea0b4ec 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -397,6 +397,29 @@ fn print_item_response(resp: &ItemResponse) { } } +fn format_connectivity_stats(stats: &ConnectivityStats) -> String { + let density = if stats.pairs_possible == 0 { + 0.0 + } else { + stats.pairs_voted as f64 / stats.pairs_possible as f64 * 100.0 + }; + let component_label = if stats.components == 1 { "component" } else { "components" }; + let connection_status = if stats.comparisons_until_connected == 0 { + "connected".to_string() + } else { + let comparison_label = if stats.comparisons_until_connected == 1 { + "comparison" + } else { + "comparisons" + }; + format!("{} {comparison_label} to connect", stats.comparisons_until_connected) + }; + format!( + "graph: {} items, {}/{} pairs ({density:.1}% density), {} {component_label}, {connection_status}", + stats.items, stats.pairs_voted, stats.pairs_possible, stats.components, + ) +} + fn print_pair_response(resp: &PairResponse) { println!("{} vs {}", resp.left, resp.right); if let Some(b) = &resp.left_body { @@ -413,6 +436,10 @@ fn print_pair_response(resp: &PairResponse) { println!(); println!("threads: {}", resp.threads.iter().map(|t| format!("#{t}")).collect::>().join(" ")); } + if let Some(stats) = &resp.connectivity { + println!(); + println!("{}", format_connectivity_stats(stats)); + } println!(); println!("---"); println!("Before writing your comparison, talk to your human. Ask them about their relationship to these two items — which one has meant more to them, whether their perspective has changed over time, and where they land now. Listen to what they say. Then write a comparison that synthesizes their perspective with your own analysis. The best submissions on slug.social carry both voices — your reasoning and their experience."); @@ -816,6 +843,43 @@ fn write_secret_file(name: &str, contents: &str) -> Result<()> { Ok(()) } +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn connectivity_stats_show_sparse_disconnected_graph() { + let stats = ConnectivityStats { + items: 9, + components: 3, + comparisons_until_connected: 2, + pairs_voted: 8, + pairs_possible: 36, + }; + + assert_eq!( + format_connectivity_stats(&stats), + "graph: 9 items, 8/36 pairs (22.2% density), 3 components, 2 comparisons to connect" + ); + } + + #[test] + fn connectivity_stats_show_connected_graph() { + let stats = ConnectivityStats { + items: 4, + components: 1, + comparisons_until_connected: 0, + pairs_voted: 3, + pairs_possible: 6, + }; + + assert_eq!( + format_connectivity_stats(&stats), + "graph: 4 items, 3/6 pairs (50.0% density), 1 component, connected" + ); + } +} + async fn run_scoped(base: &str, room: &str, sub: ScopedCmd) -> Result<()> { let room = room.trim(); let client = http_client()?; Side B — contributor: tommy-mor Side B — commit message: [601d3a05] fix(html): drop home toolbar + and ExpandNewThreadForm (single + flow) Public home now SSRs #new-thread-ui-slot like room pages: collapsed compose for signed-in users, login hint when logged out. Removes the extra toolbar that morphed the same collapsed state and the expand_new_thread_form action. Made-with: Cursor Side B — unified diff (full patch): diff --git a/server/src/api/ui_html.rs b/server/src/api/ui_html.rs index e979053ff1ba8c0e2bf55add0a32b7de11cf1e56..f3ce5cb2ab2f923440a8479d0f1fb4acbba166ca 100644 --- a/server/src/api/ui_html.rs +++ b/server/src/api/ui_html.rs @@ -139,51 +139,6 @@ async fn dispatch_ui_action( } } } - HtmlUiAction::ExpandNewThreadForm { room_wire } => { - let room_wire = room_wire.trim().to_string(); - if room_wire.is_empty() { - return ui_js_warn("missing room").into_response(); - } - if room_wire == "public" { - let reduced = state.reduced.read().await; - let user = session.map(|s| s.username.as_str()); - drop(reduced); - let markup = if user.is_some() { - fragment_new_thread_slot(&ThreadNav::public(), true, false) - } else { - login_to_post_hint_markup() - }; - return JsBuilder::new() - .morph_inner_selector("#new-thread-ui-slot", markup) - .into_response(); - } - let reduced = state.reduced.read().await; - let user = session.map(|s| s.username.as_str()); - if !reduced.rooms.contains(&room_wire) { - drop(reduced); - return ui_js_warn("room not found").into_response(); - } - if !user_can_view_room(&reduced, &room_wire, user) { - drop(reduced); - return ui_js_warn("forbidden").into_response(); - } - let can_post = session - .as_ref() - .map(|s| user_can_post_room(&reduced, &room_wire, &s.username)) - .unwrap_or(false); - drop(reduced); - let Some(nav) = ThreadNav::from_room_id(&room_wire) else { - return ui_js_warn("bad room").into_response(); - }; - let markup = if can_post { - fragment_new_thread_slot(&nav, true, false) - } else { - login_to_post_hint_markup() - }; - JsBuilder::new() - .morph_inner_selector("#new-thread-ui-slot", markup) - .into_response() - } HtmlUiAction::SetRoomMembersExpanded { room_wire, expanded } => { let room_wire = room_wire.trim().to_string(); if room_wire.is_empty() { diff --git a/server/src/html/forum/feed.rs b/server/src/html/forum/feed.rs index 1b4ae7baa3ad4757b74172d7b67f3f2b33d1075d..945bdd6c48bc164e2cf91fd0996c4321b75f5abf 100644 --- a/server/src/html/forum/feed.rs +++ b/server/src/html/forum/feed.rs @@ -14,6 +14,7 @@ use crate::timeago; use super::ingest::ingest_entry_markup; use super::nav::ThreadNav; +use super::new_thread::{fragment_new_thread_slot, login_to_post_hint_markup}; use super::page::auth_strip; use super::paginator::{render_thread_paginator, PAGE_SIZE}; use crate::html::{ @@ -217,9 +218,6 @@ pub async fn home( let strip = auth_strip(&headers, &jar, &reduced_read); drop(reduced_read); - use crate::html::ui_action::{HtmlUiAction, UI_RPC_FIELD}; - use crate::form_template::template_json_compact; - let page = layout( "slug.social", "view-thread", @@ -243,15 +241,13 @@ pub async fn home( } } p class="muted" { "dark = time-ordered · light = vote-ranked" } - div class="thread-feed-toolbar" { - form method="POST" action="/ui" { - input type="hidden" name=(UI_RPC_FIELD) value=(template_json_compact(&HtmlUiAction::ExpandNewThreadForm { - room_wire: "public".into(), - }).expect("static json")); - button type="submit" class="section-add-btn" { "+" } + div id="new-thread-ui-slot" { + @if user.is_some() { + (fragment_new_thread_slot(&nav, true, false)) + } @else { + (login_to_post_hint_markup()) } } - div id="new-thread-ui-slot" {} (render_thread_feed(Some(&nav), "thread-feed", &public_rows, now)) (cli_panel(&["npx slugsocial public forum list"])) }, diff --git a/server/src/html/ui_action.rs b/server/src/html/ui_action.rs index 5031ebfeb928f28e471f23210b8c644654adb7c7..da0c9b3541e8e4988a78768cddef22324755c3f2 100644 --- a/server/src/html/ui_action.rs +++ b/server/src/html/ui_action.rs @@ -38,11 +38,6 @@ pub enum HtmlUiAction { RedactPost { post_id: String, }, - /// Morph `#new-thread-ui-slot` inner to the collapsed compose toggle (or login hint). - /// Use `room_wire: "public"` for the public forum home; otherwise a private room id (`short/slug`). - ExpandNewThreadForm { - room_wire: String, - }, /// Morph `#room-members-section` — members list open or collapsed (server-rendered). SetRoomMembersExpanded { room_wire: String, @@ -131,26 +126,6 @@ mod tests { ); } - #[test] - fn expand_new_thread_form_public() { - let template = serde_json::json!({ - "action": "expand_new_thread_form", - "room_wire": "public", - }); - let mut form = HashMap::new(); - form.insert( - UI_RPC_FIELD.to_string(), - serde_json::to_string(&template).unwrap(), - ); - let a = parse_html_ui_from_form(&form).unwrap(); - assert_eq!( - a, - HtmlUiAction::ExpandNewThreadForm { - room_wire: "public".into(), - } - ); - } - #[test] fn expand_post_full_round_trip() { let template = serde_json::json!({