constitution · epochs · watch · epoch 3

comparison

c_0957178bd303 (tommy-mor) vs c_11d6a0ec9839 (tommy-mor)

download prompt · raw event · cmp_50bcc7f08c0d8d

council reasoning

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

Commit B makes a broader functional improvement across the application. It fixes multiple issues in the voting flow: passes delegate information separately instead of embedding it in the request text, prevents an incorrect fallback when too few sibling items exist, removes an unnecessary UI control, renames the public voting route, updates navigation and routing, propagates API signature changes, and adjusts tests accordingly. These changes affect behavior, API usage, routing, and test coverage. Commit A is a focused UI/layout fix that moves the view counter into the top navigation and updates CSS to prevent overlap with authentication controls. While useful, its scope and impact are much smaller than the multi-faceted functional changes in Commit B.

openai/gpt-5.3-chat · winner B · 1:6 · permalink

Side B makes substantial functional and architectural changes across the codebase: modifies API signatures, changes request handling, removes incorrect fallback behavior, updates routing (/vote/compare → /vote), adjusts UI logic, and updates multiple tests. Side A is a small UI/layout tweak with minor CSS adjustments. The scope and impact of B are far greater.

openai/gpt-5.2-chat · winner B · 1:6 · permalink

Commit B introduces substantial functional changes across the API, routing, UI logic, and tests: adding a delegate parameter to RPC calls, correcting vote fallback behavior, removing the swap button, renaming routes from /vote/compare to /vote, and updating integration and browser tests accordingly. These changes affect core behavior and multiple modules. In contrast, Commit A is a small UI/layout fix adjusting nav placement and CSS for the view counter. Therefore, Commit B contributed significantly more.

sides

A — c_0957178bd303 (tommy-mor)

message

[31b0a8c3] Put view count and auth links in the same top nav.

Stops the fixed view counter from overlapping the login controls.

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

diff preview

diff --git a/server/src/html/mod.rs b/server/src/html/mod.rs
index 5b9b73c7ad24499737f0576b3603c3ff5355251e..71db33abf6c1e869a48c9c331c2b74cf0fdcb576 100644
--- a/server/src/html/mod.rs
+++ b/server/src/html/mod.rs
@@ -143,10 +143,10 @@ pub(crate) fn layout(title: &str, body: Markup, views: u64, nav_user: Option<&st
                 script src="https://unpkg.com/idiomorph@0.3.0/dist/idiomorph.min.js" {}
             }
             body class="home" {
-                @if views > 0 {
-                    span class="view-meta muted" { (views) " views" }
-                }
                 nav class="top-nav" {
+                    @if views > 0 {
+                        span class="view-meta muted" { (views) " views" }
+                    }
                     @if let Some(name) = nav_user {
                         span class="top-nav-user" data-testid="nav-user" { (name) }
                         a href="/login" data-testid="nav-account" { "account" }
diff --git a/server/static/sorter.css b/server/static/sorter.css
index e7aca23b426d504938b90b5a25ad06b07843ba02..6ab384de6bd04eb77f56a5dff57c0c901e1bf24c 100644
--- a/server/static/sorter.css
+++ b/server/static/sorter.css
@@ -28,10 +28,8 @@ body {
 }
 
 .view-meta {
-  position: fixed;
-  top: 0.1rem;
-  right: 0.5rem;
   font-size: 0.75rem;
+  margin-right: auto;
 }
 
 .top-nav {

download full diff A

B — c_11d6a0ec9839 (tommy-mor)

message

[36e92cf2] Fix /vote/compare page: attribution, fallback, swap button, and route rename.

- Pass WEB_BROWSER_AGENT as delegate_opt out-of-band (not in DSL body text)
- Error instead of falling back to all items when sibling pool < 2
- Remove pointless "swap sides" button
- Rename route /vote/compare → /vote

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

diff preview

diff --git a/server/src/api/rpc.rs b/server/src/api/rpc.rs
index 0b7070a2b7289e5c40778c8d062bd4150285a8df..8e3d8e34922ddaaa89ea699edd99402bd40fdb1b 100644
--- a/server/src/api/rpc.rs
+++ b/server/src/api/rpc.rs
@@ -281,6 +281,7 @@ pub async fn rpc_post_with_bearer(
     bearer_token: &str,
     room: String,
     thread_tag: String,
+    delegate_opt: Option<String>,
     text: String,
 ) -> Result<RpcResult, RpcErr> {
     use axum::http::{header, HeaderMap, HeaderValue};
@@ -288,7 +289,7 @@ pub async fn rpc_post_with_bearer(
     let hv = HeaderValue::from_str(&format!("Bearer {bearer_token}"))
         .map_err(|_| ("invalid session token".into(), None))?;
     headers.insert(header::AUTHORIZATION, hv);
-    rpc_post(state, &headers, room, thread_tag, None, text, false).await
+    rpc_post(state, &headers, room, thread_tag, delegate_opt, text, false).await
 }
 
 async fn rpc_check(
diff --git a/server/src/api/ui_html.rs b/server/src/api/ui_html.rs
index a13a94767d8905adc09e31effff838e166781b6c..5faa642451d69555cb391974beb0ad22c9355c8c 100644
--- a/server/src/api/ui_html.rs
+++ b/server/src/api/ui_html.rs
@@ -118,6 +118,7 @@ async fn dispatch_ui_action(
                 &session.bearer,
                 room.clone(),
                 thread_tag.clone(),
+                None,
                 text,
             )
             .await
@@ -246,8 +247,7 @@ async fn dispatch_ui_action(
             }
 
             let text = format!(
-                "@{}\n{{\n{}\n}}\n{} {}:{} {}\n",
-                crate::api::auth::WEB_BROWSER_AGENT,
+                "{{\n{}\n}}\n{} {}:{} {}\n",
                 exp,
                 left_id.as_str(),
                 rl,
@@ -260,6 +260,7 @@ async fn dispatch_ui_action(
                 &session.bearer,
                 room.clone(),
                 thread_tag.clone(),
+                Some(crate::api::auth::WEB_BROWSER_AGENT.to_string()),
                 text,
             )
             .await
diff --git a/server/src/html/forum/nav.rs b/server/src/html/forum/nav.rs
index d33dc50f0b89a5ed7730c3bca204a67f6309388f..ae67f550a4bdf0bbeff58384c29a715015b64bd5 100644
--- a/server/src/html/forum/nav.rs
+++ b/server/src/html/forum/nav.rs
@@ -85,7 +85,7 @@ impl ThreadNav {
         format!("{}/{}/{}", self.thread_path_prefix, tag, idx)
     }
 
-    /// Empty for public; `/r/:seg` for room — prefix for routes like `/vote/compare`.
+    /// Empty for public; `/r/:seg` for room — prefix for routes like `/vote`.
     pub(crate) fn room_path_prefix_for_vote_compare(&self) -> String {
         match &self.scope {
             ScopeId::Public => String::new(),
diff --git a/server/src/html/garden/vote.rs b/server/src/html/garden/vote.rs
index bdb4008c29ba49d7dc97b1cd2c32d9cdfce1ca04..1d7fc8aa7436bfa9c1186dfd940a8d751ab7e088 100644
--- a/server/src/html/garden/vote.rs
+++ b/server/src/html/garden/vote.rs
@@ -208,7 +208,7 @@ pub(crate) async fn vote_compare_post_success_js(
     state: &AppState,
     nav: &ThreadNav,
     _room_wire: &str,
-    thread_tag: &str,
+    _thread_tag: &str,
     left: &ItemId,
     right: &ItemId,
     _post_id: &str,
@@ -218,8 +218,7 @@ pub(crate) async fn vote_compare_post_success_js(
     let content = content_for_garden_view(&reduced, &nav.scope());
     let edge_history = vote_edge_history_markup(content, left, right);
     let next_pair = suggest_next_vote_pair(content, left, right);
-    let nav_markup =
-        vote_compare_nav_markup(nav, next_pair.as_ref(), left, right, Some(thread_tag));
+    let nav_markup = vote_compare_nav_markup(nav, next_pair.as_ref());
     drop(reduced);
     JsBuilder::new()
         .morph_inner_selector("#vote-edge-history-region", edge_history)
@@ -236,7 +235,7 @@ pub(super) fn vote_compare_href(
     let left_q = urlencoding::encode(left.as_str());
     let right_q = urlencoding::encode(right.as_str());
     let base = format!(
-        "{}/vote/compare?left={}&right={}",
+        "{}/vote?left={}&right={}",
         nav.room_path_prefix_for_vote_compare(),
         left_q,
         right_q
@@ -251,12 +250,8 @@ pub(super) fn vote_compare_href(
 fn vote_compare_nav_markup(
     nav: &ThreadNav,
     next_pair: Option<&(ItemId, ItemId)>,
-    left: &ItemId,
-    right: &ItemId,
-    thread_override: Option<&str>,
 ) -> maud::Markup {
     let next_pair_href = next_pair.map(|(nl, nr)| vote_compare_href(nav, nl, nr, None));
-    let swap_pair_href = vote_compare_href(nav, right, left, thread_override);
     html! {
         div class="vote-compare-nav" {
             @if let Some(href) = &next_pair_href {
@@ -264,7 +259,6 @@ fn vote_compare_nav_markup(
             } @else {
                 span class="vote-compare-next is-disabled" { "no next pair" }
             }
-            a class="vote-compare-next" href=(swap_pair_href) { "swap sides" }
         }
     }
 }
@@ -274,7 +268,7 @@ pub(super) fn suggest_next_vote_pair(
     current_left: &ItemId,
     current_right: &ItemId,
 ) -> Option<(ItemId, ItemId)> {
-    let mut pool: Vec<ItemId> = if current_left.parent().as_ref().map(|p| p.as_str())
+    let pool: Vec<ItemId> = if current_left.parent().as_ref().map(|p| p.as_str())
         == current_right.parent().as_ref().map(|p| p.as_str())
     {
         current_left
@@ -291,7 +285,7 @@ pub(super) fn suggest_next_vote_pair(
         Vec::new()
     };
     if pool.len() < 2 {
-        pool = content.items.iter().cloned().collect();
+        return None;
     }
     suggest_next_pair_in_pool(
         &content.ranking_group,
@@ -334,7 +328,7 @@ pub struct VoteCompareQuery {
     pub thread: Option<String>,
 }
 
-/// Public pairwise vote UI — `/vote/compare?left=&right=&thread=`.
+/// Public pairwise vote UI — `/vote?left=&right=&thread=`.
 pub async fn vote_compare_page(
     State(state): State<AppState>,
     Query(q): Query<VoteCompareQuery>,
@@ -426,7 +420,7 @@ async fn vote_compare_inner(
     let next_path = uri
         .path_and_query()
         .map(|pq| pq.as_str().to_string())
-        .unwrap_or_else(|| "/vote/compare".into());
+        .unwrap_or_else(|| "/vote".into());
 
     let rpc_json = template_json_compact(&json!({
         "action": "vote_compare_post",
@@ -462,7 +456,7 @@ async fn vote_compare_inner(
                 Some(&item_bodies_for_cards),
             ))
         }
-        (vote_compare_nav_markup(&nav, next_pair.as_ref(), &left, &right, q.thread.as_deref()))
+        (vote_compare_nav_markup(&nav, next_pair.as_ref()))
         div id="vote-edge-history-region" {
             (edge_history)
         }
diff --git a/server/src/html/ui_action.rs b/server/src/html/ui_action.rs
index c7b643c8dde37dd1e3001f5ff716fe4f79d24c5e..dd5294202569454acf9f41b458718700bbf96f39 100644
--- a/server/src/html/ui_action.rs
+++ b/server/src/html/ui_action.rs
@@ -38,7 +38,7 @@ pub enum HtmlUiAction {
         #[serde(default)]
         form_id: Option<String>,
     },
-    /// Post a pairwise vote from `/vote/compare` (browser compose).
+    /// Post a pairwise vote from `/vote` (browser compose).
     VoteComparePost {
         room: String,
         thread_tag: String,
diff --git a/server/src/lib.rs b/server/src/lib.rs
index ad8e31099c807fb5844acb16cd5086a2f19327a7..e102e73ec167fa35dcc22d4ec8c6f40202982429 100644
--- a/server/src/lib.rs
+++ b/server/src/lib.rs
@@ -82,9 +82,9 @@ pub fn create_app(state: AppState) -> Router {
         .route("/u/:username", get(crate::html::user_profile_page))
         .route("/try", get(crate::html::editor_page))
         .route("/try/check", post(crate::html::editor_check))
-        .route("/vote/compare", get(crate::html::vote_compare_page))
+        .route("/vote", get(crate::html::vote_compare_page))
         .route(
-            "/r/:room_key/vote/compare",
+            "/r/:room_key/vote",
             get(crate::html::room_vote_compare_page),
         )
         .route("/~", get(crate::html::garden_index))
diff --git a/server/tests/integration_html.rs b/server/tests/integration_html.rs
index a25a456c7cafd74a69e65e0c2e8ed2bfb2e27498..b3e31376737b2d5aabb739f408984a1f5ae0c974 100644
--- a/server/tests/integration_html.rs
+++ b/server/tests/integration_html.rs
@@ -52,12 +52,12 @@ async fn test_view_counts_increment_and_display() {
         .normalized_storage()
         .to_storage_string();
     let vote_q_right_first = format!(
-        "/vote/compare?right={}&left={}",
+        "/vote?right={}&left={}",
         urlencoding::encode(&right),
         urlencoding::encode(&left)
     );
     let vote_q_left_first = format!(
-        "/vote/compare?left={}&right={}",
+        "/vote?left={}&right={}",
         urlencoding::encode(&left),
         urlencoding::encode(&right)
     );
@@ -148,7 +148,7 @@ https://github.com/ghvotehi/a/issues/10 {\n\
         .normalized_storage()
         .to_storage_string();
     let q = format!(
-        "/vote/compare?left={}&right={}",
+        "/vote?left={}&right={}",
         urlencoding::encode(&left),
         urlencoding::encode(&right)
     );
diff --git a/server/tests/integration_ui.rs b/server/tests/integration_ui.rs
index 091afd922d28f48ab36aa9b9084aa825036d1f74..23db7b5672418d5bb0ab7529e05ef1f89e59062a 100644
--- a/server/tests/integration_ui.rs
+++ b/server/tests/integration_ui.rs
@@ -110,7 +110,7 @@ async fn test_choose_username_carries_redirect_next() {
             .expect("pending session must exist");
         pending.provider = Some("google".to_string());
         pending.provider_id = Some("google-user-redirect".to_string());
-        pending.redirect_next = Some("/vote/compare?left=%7E%2Fa&right=%7E%2Fb".to_string());
+        pending.redirect_next = Some("/vote?left=%7E%2Fa&right=%7E%2Fb".to_string());
     }
 
     let choose = client
@@ -122,7 +122,7 @@ async fn test_choose_username_carries_redirect_next() {
 
     assert_eq!(choose.status(), reqwest::StatusCode::OK);
     let body = choose.text().await.unwrap();
-    assert!(body.contains("window.location = \"/vote/compare?left=%7E%2Fa&right=%7E%2Fb\""));
+    assert!(body.contains("window.location = \"/vote?left=%7E%2Fa&right=%7E%2Fb\""));
 }
 
 #[tokio::test]
diff --git a/test/browser_vote_compare.clj b/test/browser_vote_compare.clj
index 7cd1e467fdd8a4480df1be2afd78adb45b9cdc6d..a98e144756421e1fa5064db6a9fc0dd8b08471c7 100644
--- a/test/browser_vote_compare.clj
+++ b/test/browser_vote_compare.clj
@@ -66,7 +66,7 @@
                       :headers {"Authorization" (str "Bearer " alice-token)})
            post-json (json/parse-string (:body post-resp) false)
            _ (is (true? (get-in post-json ["results" 0 "ok"])) "seed items + edge vote via rpc")
-           cmp-url (str base-url "/vote/compare?left=" (enc left-url) "&right=" (enc third-url))]
+           cmp-url (str base-url "/vote?left=" (enc left-url) "&right=" (enc third-url))]
        (core/with-playwright [pw]
          (core/with-browser [browser (core/launch-chromium pw {:headless true :channel "chrome"})]
            (core/with-context [ctx (core/new-context browser)]

download full diff B

Hardlinks — judgments / attempts / prompt

prompt download

judgments

attempts

Prompt text is loaded only by the download route.