constitution · epochs · watch · epoch 3

comparison

c_bc8c17a00ed7 (tommy-mor) vs c_f515f8a12d7a (tommy-mor)

download prompt · raw event · cmp_f7219ea2026e6e

council reasoning

~anthropic/claude-sonnet-latest · winner A · 6:4 · permalink

Side A fixes a real functional bug (pinned/stickied posts polluting import results) with a targeted implementation plus a dedicated regression test. Side B is a reasonable cleanup/simplification (removing a redundant toolbar action and always-SSR-ing the compose slot) but is more of a refactor/dead-code removal without a new capability or bugfix, and adds no new tests.

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

B removes a redundant ExpandNewThreadForm action, handler, toolbar POST, and tests, SSR-ing #new-thread-ui-slot on home like room pages so compose is one consistent path instead of collapsed-then-morph. A is a correct, tested filter for stickied/pinned Reddit children, but it is a narrow import tweak versus B’s lasting UI simplification and deleted surface area.

openai/gpt-chat-latest · winner A · 4:3 · permalink

Side A fixes a concrete data-import issue by filtering stickied/pinned Reddit posts during subreddit listing parsing, adds a dedicated helper, and includes a regression test covering both `stickied` and `pinned` flags. Side B simplifies the HTML/UI flow by removing the `ExpandNewThreadForm` action and server-rendering the compose slot on the home page, but it is primarily a refactor and UX consolidation rather than a functional correctness improvement.

sides

A — c_bc8c17a00ed7 (tommy-mor)

message

[03cd8f2e] Skip pinned Reddit posts when importing subreddit listings.

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

diff preview

diff --git a/server/src/reddit.rs b/server/src/reddit.rs
index f409764c1e1f36216f1b08107043c2eab905694c..fa5577f8ee0a8c53ff4dbec988a90a5d2fc3cdee 100644
--- a/server/src/reddit.rs
+++ b/server/src/reddit.rs
@@ -661,6 +661,7 @@ pub fn map_children_url(id: &ItemId, api_base: &str) -> String {
 /// Parse a subreddit listing payload into `(child_id, child_payload)` entries.
 /// Each child id is the post's permalink under `reddit.com/…`, and the payload
 /// is the raw `{kind, data}` listing element (persisted per child).
+/// Pinned / stickied posts are skipped.
 fn parse_children(_parent: &ItemId, payload: &Value) -> Vec<(ItemId, Value)> {
     let mut out = Vec::new();
     let children = match payload.pointer("/data/children").and_then(|c| c.as_array()) {
@@ -668,6 +669,9 @@ fn parse_children(_parent: &ItemId, payload: &Value) -> Vec<(ItemId, Value)> {
         None => return out,
     };
     for child in children {
+        if child_is_pinned(child) {
+            continue;
+        }
         let permalink = match child.pointer("/data/permalink").and_then(|p| p.as_str()) {
             Some(p) if !p.is_empty() => p,
             _ => continue,
@@ -680,6 +684,15 @@ fn parse_children(_parent: &ItemId, payload: &Value) -> Vec<(ItemId, Value)> {
     out
 }
 
+fn child_is_pinned(child: &Value) -> bool {
+    let data = match child.get("data") {
+        Some(d) => d,
+        None => return false,
+    };
+    data.get("stickied").and_then(|v| v.as_bool()) == Some(true)
+        || data.get("pinned").and_then(|v| v.as_bool()) == Some(true)
+}
+
 fn parse_reddit_view(id: &ItemId, v: &Value) -> Option<crate::reducer::EntityData> {
     let segments: Vec<&str> = id.as_str().split('/').collect();
 
@@ -853,4 +866,46 @@ mod tests {
             Some("http://v3.redgifs.com/watch/impossibleprestigioushedgehog")
         );
     }
+
+    #[test]
+    fn parse_children_skips_pinned_posts() {
+        let payload = serde_json::json!({
+            "kind": "Listing",
+            "data": {
+                "children": [
+                    {
+                        "kind": "t3",
+                        "data": {
+                            "title": "Official rules (pinned)",
+                            "permalink": "/r/rust/comments/pin/official_rules/",
+                            "stickied": true
+                        }
+                    },
+                    {
+                        "kind": "t3",
+                        "data": {
+                            "title": "Also pinned via pinned field",
+                            "permalink": "/r/rust/comments/pin2/also_pinned/",
+                            "pinned": true
+                        }
+                    },
+                    {
+                        "kind": "t3",
+                        "data": {
+                            "title": "Normal post",
+                            "permalink": "/r/rust/comments/aaa/normal_post/",
+                            "stickied": false
+                        }
+                    }
+                ]
+            }
+        });
+        let parent = ItemId::from_url("https://reddit.com/r/rust").unwrap();
+        let children = parse_children(&parent, &payload);
+        assert_eq!(children.len(), 1);
+        assert_eq!(
+            children[0].0.as_str(),
+            "https://reddit.com/r/rust/comments/aaa"
+        );
+    }
 }

download full diff A

B — c_f515f8a12d7a (tommy-mor)

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

diff preview

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!({

download full diff B

Hardlinks — judgments / attempts / prompt

prompt download

judgments

attempts

Prompt text is loaded only by the download route.