constitution · epochs · watch · epoch 3

comparison

c_16438843de8f (tommy-mor) vs c_6864b1ca8ce6 (tommy-mor)

download prompt · raw event · cmp_5877bdd680b029

council reasoning

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

Side A adds real functional infrastructure — a JSONL event log with load/append logic, a persistent view-counter store with debounced disk flushing, plus deployment scaffolding (Dockerfile, fly.toml, deps.edn) — which are concrete building blocks for persistence and deployment. Side B is purely cosmetic CSS polish (padding, focus outlines, color-mix accents) duplicated across two theme files, improving UX consistency but carrying no functional or architectural weight.

~x-ai/grok-latest · winner A · 5:1 · permalink

A adds lasting foundation: EventLog JSONL persistence, ViewStore with async flush, and real deploy plumbing (Dockerfile + fly.toml). B only restyles voting/pin UI in CSS with no behavioral or architectural change.

openai/gpt-chat-latest · winner A · 9:1 · permalink

Side A introduces substantive project infrastructure and persistence: a Dockerfile and Fly deployment config, a reusable EventLog module that appends and reloads JSONL events with error handling and corrupt-line tolerance, and a ViewStore that persists view counts atomically. Side B is almost entirely CSS refinements for the voting UI (spacing, colors, focus states, layout, and theme overrides), improving appearance and accessibility but not adding comparable core functionality.

sides

A — c_16438843de8f (tommy-mor)

message

[4cd0d15d] more seed

diff preview

diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..9cb07c60cb0da063f747cfbf1b3b876ecb8ba03e
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,34 @@
+# time 0.3.47+ requires Rust 1.88 (edition 2024)
+FROM rust:1.88-slim as builder
+
+WORKDIR /build
+
+RUN apt-get update && \
+    apt-get install -y pkg-config libssl-dev && \
+    rm -rf /var/lib/apt/lists/*
+
+# Copy source and build. (Keep it simple to avoid remote build cache oddities.)
+COPY . .
+RUN cargo build --release --package slugsocial-server
+
+FROM debian:bookworm-slim
+
+RUN apt-get update && \
+    apt-get install -y ca-certificates && \
+    rm -rf /var/lib/apt/lists/*
+
+WORKDIR /app
+
+COPY --from=builder /build/target/release/slugsocial-server /app/slugsocial-server
+
+# Create data directory for persistent volume
+RUN mkdir -p /data
+
+ENV SLUG_DATA_DIR=/data
+ENV SLUG_EVENT_LOG=/data/events.jsonl
+ENV PORT=8080
+
+EXPOSE 8080
+
+CMD ["/app/slugsocial-server"]
+
diff --git a/deps.edn b/deps.edn
new file mode 100644
index 0000000000000000000000000000000000000000..0bf892d44f491cb2313e01ae8a942c3097c52948
--- /dev/null
+++ b/deps.edn
@@ -0,0 +1,10 @@
+{:paths ["." "test"]
+ :deps {cheshire/cheshire {:mvn/version "5.13.0"}
+        http-kit/http-kit {:mvn/version "2.8.0"}
+        babashka/fs {:mvn/version "0.5.32"}
+        babashka/process {:mvn/version "0.6.25"}
+        com.blockether/spel {:mvn/version "0.7.11"}}
+ :aliases
+ {:kaocha {:extra-deps {lambdaisland/kaocha {:mvn/version "1.91.1392"}
+                        lambdaisland/kaocha-junit-xml {:mvn/version "1.17.101"}}
+          :main-opts ["-m" "kaocha.runner"]}}}
diff --git a/event_log.rs b/event_log.rs
new file mode 100644
index 0000000000000000000000000000000000000000..eaae0d495e43a45d6590603892265a62cc92906e
--- /dev/null
+++ b/event_log.rs
@@ -0,0 +1,83 @@
+use std::path::{Path, PathBuf};
+
+use tokio::{
+    fs::{self, OpenOptions},
+    io::{AsyncBufReadExt, AsyncWriteExt, BufReader},
+};
+
+use crate::events::Event;
+
+#[derive(Debug, thiserror::Error)]
+pub enum EventLogError {
+    #[error("io error: {0}")]
+    Io(#[from] std::io::Error),
+    #[error("json error: {0}")]
+    Json(#[from] serde_json::Error),
+}
+
+#[derive(Debug, Clone)]
+pub struct EventLog {
+    path: PathBuf,
+}
+
+impl EventLog {
+    pub fn new(path: impl Into<PathBuf>) -> Self {
+        Self { path: path.into() }
+    }
+
+    pub fn path(&self) -> &Path {
+        &self.path
+    }
+
+    pub async fn ensure_parent_dir(&self) -> Result<(), EventLogError> {
+        if let Some(parent) = self.path.parent() {
+            fs::create_dir_all(parent).await?;
+        }
+        Ok(())
+    }
+
+    pub async fn append(&self, event: &Event) -> Result<(), EventLogError> {
+        self.ensure_parent_dir().await?;
+        let mut f: tokio::fs::File = OpenOptions::new()
+            .create(true)
+            .append(true)
+            .open(&self.path)
+            .await?;
+
+        let mut line = serde_json::to_string(event)?;
+        line.push('\n');
+        f.write_all(line.as_bytes()).await?;
+        f.flush().await?;
+        Ok(())
+    }
+
+    /// Load events from JSONL. Corrupt lines are skipped and returned as `(line_no, line)`.
+    pub async fn load_all(&self) -> Result<(Vec<Event>, Vec<(usize, String)>), EventLogError> {
+        if !fs::try_exists(&self.path).await? {
+            return Ok((vec![], vec![]));
+        }
+
+        let f = fs::File::open(&self.path).await?;
+        let mut reader = BufReader::new(f).lines();
+
+        let mut events = Vec::new();
+        let mut bad_lines = Vec::new();
+
+        let mut line_no: usize = 0;
+        while let Some(line) = reader.next_line().await? {
+            line_no += 1;
+            let trimmed = line.trim();
+            if trimmed.is_empty() {
+                continue;
+            }
+            match serde_json::from_str::<Event>(trimmed) {
+                Ok(ev) => events.push(ev),
+                Err(_) => bad_lines.push((line_no, line)),
+            }
+        }
+
+        Ok((events, bad_lines))
+    }
+}
+
+
diff --git a/fly.toml b/fly.toml
new file mode 100644
index 0000000000000000000000000000000000000000..bbb9345e527452db1d87a549213645c195eae5fc
--- /dev/null
+++ b/fly.toml
@@ -0,0 +1,42 @@
+app = "slugsocial"
+primary_region = "iad"
+
+[build]
+  dockerfile = "Dockerfile"
+
+[env]
+  SLUG_DATA_DIR = "/data"
+  SLUG_EVENT_LOG = "/data/events.jsonl"
+  PORT = "8080"
+
+[[services]]
+  internal_port = 8080
+  protocol = "tcp"
+
+  [[services.ports]]
+    port = 80
+    handlers = ["http"]
+    force_https = true
+
+  [[services.ports]]
+    port = 443
+    handlers = ["tls", "http"]
+
+  [services.concurrency]
+    type = "connections"
+    hard_limit = 1000
+    soft_limit = 500
+
+  [[services.http_checks]]
+    interval = "10s"
+    timeout = "2s"
+    grace_period = "5s"
+    method = "GET"
+    path = "/healthz"
+    protocol = "http"
+    tls_skip_verify = false
+
+[[mounts]]
+  source = "slugsocial_data"
+  destination = "/data"
+
diff --git a/views.rs b/views.rs
new file mode 100644
index 0000000000000000000000000000000000000000..d4f0ffc49475f014698b4da0de6f476884430813
--- /dev/null
+++ b/views.rs
@@ -0,0 +1,63 @@
+use std::{
+    collections::HashMap,
+    sync::{Arc, Mutex},
+};
+use tokio::sync::mpsc;
+
+type CountMap = Arc<Mutex<HashMap<String, u64>>>;
+
+#[derive(Clone)]
+pub struct ViewStore {
+    counts: CountMap,
+    flush_tx: mpsc::Sender<()>,
+}
+
+impl ViewStore {
+    pub fn new(json_path: &str) -> Self {
+        // Load existing counts from disk on startup (best-effort)
+        let initial: HashMap<String, u64> = std::fs::read_to_string(json_path)
+            .ok()
+            .and_then(|s| serde_json::from_str(&s).ok())
+            .unwrap_or_default();
+
+        let counts: CountMap = Arc::new(Mutex::new(initial));
+        let (flush_tx, mut flush_rx) = mpsc::channel::<()>(64);
+        let path = json_path.to_string();
+
+        let counts_for_writer = counts.clone();
+        tokio::spawn(async move {
+            while flush_rx.recv().await.is_some() {
+                while flush_rx.try_recv().is_ok() {}
+
+                let snapshot: HashMap<String, u64> = {
+                    counts_for_writer.lock().unwrap().clone()
+                };
+
+                let path = path.clone();
+                let _ = tokio::task::spawn_blocking(move || {
+                    if let Ok(json) = serde_json::to_string(&snapshot) {
+                        let tmp = format!("{path}.tmp");
+                        if std::fs::write(&tmp, &json).is_ok() {
+                            let _ = std::fs::rename(&tmp, &path);
+                        }
+                    }
+                })
+                .await;
+            }
+        });
+
+        Self { counts, flush_tx }
+    }
+
+    pub fn increment(&self, path: String) {
+        {
+            let mut map = self.counts.lock().unwrap();
+            *map.entry(path).or_insert(0) += 1;
+        }
+        let _ = self.flush_tx.try_send(());
+    }
+
+    pub fn get_views(&self, path: &str) -> u64 {
+        self.counts.lock().unwrap().get(path).copied().unwrap_or(0)
+    }
+}

download full diff A

B — c_6864b1ca8ce6 (tommy-mor)

message

[51e73d38] voting looks better?

diff preview

diff --git a/server/static/theme_default.css b/server/static/theme_default.css
index 5f60ff7a8cf9045d8d43228baad8ba80ad057f2a..ec0fbe7acee0aa2802f978f14a9b0fc86e78c5b8 100644
--- a/server/static/theme_default.css
+++ b/server/static/theme_default.css
@@ -844,18 +844,25 @@ a.ont-vote-compare-btn {
   color: var(--ui);
   cursor: pointer;
   font-size: 11px;
-  padding: 2px 8px;
+  padding: 3px 10px;
   text-decoration: none;
   display: inline-flex;
   align-items: center;
   gap: 4px;
   font-family: inherit;
+  border-radius: 2px;
 }
 button.ont-pin-btn:hover,
 a.ont-vote-compare-btn:hover { color: var(--signal); }
+button.ont-pin-btn:focus-visible,
+a.ont-vote-compare-btn:focus-visible {
+  outline: 2px solid var(--link);
+  outline-offset: 2px;
+}
 button.ont-pin-btn-active {
   border-color: var(--link);
   color: var(--signal);
+  background: color-mix(in srgb, var(--link) 12%, var(--g4));
 }
 .ont-garden-child-actions {
   display: inline-flex;
@@ -868,12 +875,33 @@ a.ont-garden-vote-ico,
 span.ont-garden-pinned-here {
   font-size: 13px;
   line-height: 1;
-  padding: 0 2px;
-  border: none;
-  background: transparent;
+  padding: 2px 6px;
+  min-width: 26px;
+  min-height: 26px;
+  box-sizing: border-box;
+  border: var(--bv) solid;
+  border-color: var(--hi) var(--lo) var(--lo) var(--hi);
+  border-radius: 2px;
+  background: var(--g3);
   cursor: pointer;
   text-decoration: none;
   color: inherit;
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+}
+a.ont-garden-vote-ico:hover {
+  color: var(--signal);
+  background: var(--g4);
+}
+span.ont-garden-pinned-here {
+  border-color: var(--link);
+  color: var(--signal);
+  cursor: default;
+}
+button.ont-garden-pin-ico:focus-visible {
+  outline: 2px solid var(--link);
+  outline-offset: 2px;
 }
 body.view-ontology-light ol.ont-ranking-list li,
 body.view-ontology-light ul.ont-group-list li {
@@ -889,8 +917,19 @@ body.view-ontology-light ul.ont-group-list li .item-link {
 
 /* Vote compare page */
 .vote-compare-shell {
-  margin: 12px 0;
+  margin: 12px 0 20px;
   max-width: 720px;
+  padding: 14px 16px 18px;
+  background: var(--g2);
+  border: var(--bv-lg) solid;
+  border-color: var(--hi) var(--lo) var(--lo) var(--hi);
+}
+body.view-vote-compare .vote-compare-shell > h2 {
+  margin-top: 0;
+  font-size: 12px;
+  letter-spacing: 0.12em;
+  text-transform: uppercase;
+  color: var(--meta);
 }
 .vote-compare-pair {
   display: flex;
@@ -899,12 +938,20 @@ body.view-ontology-light ul.ont-group-list li .item-link {
   gap: 10px 16px;
   margin: 10px 0;
 }
+.vote-compare-item {
+  text-decoration: none;
+}
 .vote-compare-item code {
   font-size: 13px;
 }
+.vote-compare-item:hover code {
+  color: var(--signal);
+}
 .vote-compare-vs {
   color: var(--meta);
-  font-size: 12px;
+  font-size: 11px;
+  letter-spacing: 0.1em;
+  text-transform: uppercase;
 }
 .vote-compare-slider-label {
   display: flex;
@@ -917,7 +964,7 @@ body.view-ontology-light ul.ont-group-list li .item-link {
 #vote-preference-slider {
   flex: 1 1 180px;
   min-width: 120px;
-  accent-color: var(--ui);
+  accent-color: var(--link);
 }
 .vote-thread-picker {
   margin: 10px 0;
@@ -931,9 +978,17 @@ body.view-ontology-light ul.ont-group-list li .item-link {
 }
 #vote-thread-select {
   min-width: 160px;
+  background: var(--g3);
+  border: var(--bv) solid;
+  border-color: var(--hi) var(--lo) var(--lo) var(--hi);
+  color: var(--ui);
+  font-size: 12px;
+  padding: 3px 8px;
 }
 #vote-edge-history-region {
   margin: 14px 0 18px;
+  padding-bottom: 8px;
+  border-bottom: 1px dashed var(--lo);
 }
 .vote-compare-preview-wrap {
   margin: 12px 0;
@@ -946,8 +1001,37 @@ body.view-ontology-light ul.ont-group-list li .item-link {
 #vote-compare-preview {
   min-height: 48px;
 }
+.vote-explain-label {
+  display: block;
+  font-size: 12px;
+  color: var(--meta);
+  margin: 12px 0 4px;
+}
+body.view-vote-compare #vote-explain {
+  width: 100%;
+  max-width: 100%;
+  box-sizing: border-box;
+  background: var(--g3);
+  border: var(--bv) solid;
+  border-color: var(--hi) var(--lo) var(--lo) var(--hi);
+  color: var(--prose);
+  font: inherit;
+  line-height: 1.45;
+  padding: 8px 10px;
+}
+body.view-vote-compare #vote-explain:focus {
+  outline: none;
+  border-color: var(--link);
+  box-shadow: 0 0 0 2px color-mix(in srgb, var(--link) 22%, transparent);
+}
+body.view-vote-compare #vote-compare-form button[type="submit"] {
+  margin-top: 6px;
+}
 .vote-edge-history-title {
-  font-size: 13px;
+  font-size: 12px;
+  letter-spacing: 0.06em;
+  text-transform: uppercase;
+  color: var(--meta);
   margin: 16px 0 8px;
 }
 ol.vote-edge-history {
@@ -968,6 +1052,8 @@ li.vote-edge-history-row {
 .vote-edge-bar {
   margin-top: 4px;
   max-width: 100%;
+  border-radius: 2px;
+  overflow: hidden;
 }
 .vote-edge-reason {
   margin-top: 4px;
diff --git a/server/static/theme_retro_craft.css b/server/static/theme_retro_craft.css
index 3853e9edaf4c77c62dcf350ff28920448dd943a8..092d4b3540b052cc86870179e4c264541c4446cc 100644
--- a/server/static/theme_retro_craft.css
+++ b/server/static/theme_retro_craft.css
@@ -587,3 +587,337 @@ body.view-ontology #controls {
     padding-right: 1.25rem;
   }
 }
+
+/* ----------------------------------------------------------------
+   Ontology garden: pin HUD, row pins, vote-compare
+   (Global craft `code` + `button[type=submit]` are tuned for dark
+   thread pages; these overrides match the cream ontology shell.)
+   ---------------------------------------------------------------- */
+body.view-ontology #slug-pin-hud.slug-pin-hud {
+  margin-left: auto;
+  max-width: min(42vw, 280px);
+  font-family: var(--font-ui);
+  font-size: 0.72rem;
+  color: #5c574e;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+body.view-ontology .slug-pin-hud-link {
+  color: #1a4a8c;
+  text-decoration: none;
+  display: inline-flex;
+  align-items: center;
+  gap: 0.25rem;
+}
+body.view-ontology .slug-pin-hud-link:hover {
+  color: #0d3d82;
+  text-decoration: underline;
+}
+body.view-ontology .slug-pin-hud-glyph {
+  font-size: 0.95rem;
+  line-height: 1;
+}
+
+body.view-ontology .ont-item-meta {
+  flex-wrap: wrap;
+  align-items: center;
+  gap: 0.35rem;
+}
+body.view-ontology .ont-item-title {
+  flex: 1 1 auto;
+  min-width: 0;
+}
+body.view-ontology .ont-item-pin-zone {
+  flex: 0 0 auto;
+  margin-left: auto;
+  display: flex;
+  align-items: center;
+  gap: 0.4rem;
+}
+
+body.view-ontology button.ont-pin-btn,
+body.view-ontology a.ont-vote-compare-btn {
+  background: #ebe6dc;
+  border: 1px solid #c8c4bc;
+  border-radius: 2px;
+  color: #3d3a34;
+  cursor: pointer;
+  font-family: var(--font-ui);
+  font-size: 0.72rem;
+  letter-spacing: 0.04em;
+  padding: 0.25rem 0.55rem;
+  text-decoration: none;
+  display: inline-flex;
+  align-items: center;
+  gap: 0.3rem;
+}
+body.view-ontology button.ont-pin-btn:hover,
+body.view-ontology a.ont-vote-compare-btn:hover {
+  border-color: #a68e6b;
+  color: #1a1814;
+}
+body.view-ontology button.ont-pin-btn-active {
+  border-color: #1a4a8c;
+  background: color-mix(in srgb, #1a4a8c 12%, #ebe6dc);
+  color: #0d2d5c;
+}
+body.view-ontology button.ont-pin-btn:focus-visible,
+body.view-ontology a.ont-vote-compare-btn:focus-visible {
+  outline: 2px solid #1a4a8c;
+  outline-offset: 2px;
+}
+
+body.view-ontology .ont-garden-child-actions {
+  display: inline-flex;
+  align-items: center;
+  margin-right: 0.35rem;
+  vertical-align: middle;
+  gap: 0.1rem;
+}
+body.view-ontology button.ont-garden-pin-ico,
+body.view-ontology a.ont-garden-vote-ico,
+body.view-ontology span.ont-garden-pinned-here {
+  font-size: 0.95rem;
+  line-height: 1;
+  padding: 0.1rem 0.35rem;
+  min-width: 1.65rem;
+  min-height: 1.65rem;
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  border: 1px solid #c8c4bc;
+  border-radius: 2px;
+  background: #f7f3eb;
+  cursor: pointer;
+  text-decoration: none;
+  color: #1a1814;
+}
+body.view-ontology a.ont-garden-vote-ico:hover {
+  border-color: #a68e6b;
+  background: #ebe6dc;
+}
+body.view-ontology span.ont-garden-pinned-here {
+  border-color: #1a4a8c;
+  background: color-mix(in srgb, #1a4a8c 10%, #f7f3eb);
+  cursor: default;
+}
+body.view-ontology form.ont-garden-pin-form {
+  display: inline;
+  margin: 0;
+  padding: 0;
+}
+body.view-ontology button.ont-garden-pin-ico:focus-visible {
+  outline: 2px solid #1a4a8c;
+  outline-offset: 2px;
+}
+
+body.view-ontology ol.ont-ranking-list li,
+body.view-ontology ul.ont-group-list li {
+  display: flex;
+  align-items: baseline;
+  gap: 0.35rem;
+}
+body.view-ontology ol.ont-ranking-list li .item-link,
+body.view-ontology ul.ont-group-list li .item-link {
+  flex: 1;
+  min-width: 0;
+}
+
+body.view-ontology code,
+body.view-ontology .vote-compare-item code,
+body.view-ontology .vote-edge-meta code {
+  background: #ebe6dc;
+  color: #1a1814;
+  border: 1px solid #d4cfc4;
+  padding: 0.12em 0.35em;
+  font-size: 0.88em;
+}
+
+body.view-ontology div.ratio-bar {
+  background: #e3ded4;
+  border: 1px solid #c8c4bc;
+}
+body.view-ontology div.ratio-left,
+body.view-ontology div.ratio-right {
+  background: #cec9bf;
+}
+body.view-ontology div.ratio-left.current,
+body.view-ontology div.ratio-right.current {
+  background: #9a7b4a;
+}
+
+body.view-ontology.view-vote-compare .vote-compare-shell {
+  margin: 0.75rem 0 1.5rem;
+  max-width: 36rem;
+  padding: 0.85rem 1rem 1.1rem;
+  background: #f7f3eb;
+  border: 1px solid #c8c4bc;
+  border-radius: 3px;
+  box-shadow: 0 1px 0 rgba(26, 24, 20, 0.06);
+}
+body.view-ontology.view-vote-compare .vote-compare-shell > h2 {
+  margin-top: 0;
+  font-size: 0.7rem;
+  letter-spacing: 0.16em;
+  text-transform: uppercase;
+  color: #5c574e;
+}
+body.view-ontology .vote-compare-pair {
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  gap: 0.65rem 1rem;
+  margin: 0.65rem 0 0.85rem;
+}
+body.view-ontology .vote-compare-vs {
+  color: #8a857a;
+  font-family: var(--font-ui);
+  font-size: 0.72rem;
+  letter-spacing: 0.1em;
+  text-transform: uppercase;
+}
+body.view-ontology .vote-compare-item {
+  text-decoration: none;
+}
+body.view-ontology .vote-compare-item:hover code {
+  border-color: #a68e6b;
+  background: #f0ebe3;
+}
+
+body.view-ontology .vote-thread-picker {
+  margin: 0.85rem 0;
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  gap: 0.5rem 0.75rem;
+}
+body.view-ontology .vote-thread-picker-label {
+  font-family: var(--font-ui);
+  font-size: 0.72rem;
+  letter-spacing: 0.08em;
+  text-transform: uppercase;
+  color: #5c574e;
+}
+body.view-ontology #vote-thread-select {
+  min-width: 10rem;
+  background: #f7f3eb;
+  border: 1px solid #c8c4bc;
+  color: #1a1814;
+  font-family: var(--font-ui);
+  font-size: 0.78rem;
+  padding: 0.3rem 0.45rem;
+}
+
+body.view-ontology .vote-compare-slider-label {
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  gap: 0.5rem 0.65rem;
+  margin: 0.85rem 0;
+  font-family: var(--font-ui);
+  font-size: 0.72rem;
+  color: #5c574e;
+}
+body.view-ontology #vote-preference-slider {
+  flex: 1 1 11rem;
+  min-width: 8rem;
+  accent-color: #9a7b4a;
+}
+
+body.view-ontology .vote-explain-label {
+  display: block;
+  font-family: var(--font-ui);
+  font-size: 0.72rem;
+  letter-spacing: 0.08em;
+  text-transform: uppercase;
+  color: #5c574e;
+  margin: 0.65rem 0 0.35rem;
+}
+body.view-ontology #vote-compare-form textarea,
+body.view-ontology.view-vote-compare textarea#vote-explain {
+  background: #fdfcfa;
+  border: 1px solid #c8c4bc;
+  color: #1a1814;
+  font-family: var(--font-prose);
+  font-size: 0.9rem;
+  line-height: 1.45;
+  width: 100%;
+  max-width: 100%;
+  padding: 0.55rem 0.65rem;
+  margin-top: 0.25rem;
+}
+body.view-ontology #vote-compare-form textarea:focus {
+  border-color: #a68e6b;
+  outline: none;
+  box-shadow: 0 0 0 2px color-mix(in srgb, #a68e6b 28%, transparent);
+}
+
+body.view-ontology #vote-compare-form button[type="s

… preview truncated; 1,584 characters omitted

download full diff B

Hardlinks — judgments / attempts / prompt

prompt download

judgments

attempts

Prompt text is loaded only by the download route.