Side B introduces a substantial, well-tested feature: a content-addressed, hash-chained evidence ledger with resumable ranking, HTML index pages, credential-stripping, and comprehensive new tests (test_evidence.py, integration.clj updates), representing real architectural and durability improvements. Side A is a small, narrowly scoped UI tweak adding a view-count parameter to one page with a matching test, useful but far more limited in scope and lasting impact.
constitution · epochs · watch · epoch 3
c_5bc77fbfad41 (tommy-mor) vs c_efa306aa8040 (tommy-mor)
download prompt · raw event · cmp_d256327f659f56
council reasoning
A only wires an existing view-count into the vote-compare layout (a few call-site lines plus assertion updates). B adds a content-addressed Evidence ledger, idempotent append/hash-chaining, resumable ranking that skips duplicate LLM calls, and a full crawlable HTML evidence graph (epochs/commits/comparisons/attempts/judgments) with substantial tests—core lasting transparency infrastructure versus a minor display tweak.
Side B introduces a substantial evidence and audit infrastructure: it adds versioned append-only Evidence events, idempotent persistence with hash chaining, resumable LLM ranking, HTML evidence browsing, richer audit links, and extensive integration/unit tests covering replay, downloads, and legacy behavior. Side A is a useful but localized feature that threads existing view-count support into the vote comparison page and adds tests verifying the displayed count increments.
sides
A — c_5bc77fbfad41 (tommy-mor)
message
[d1912140] viewcount in votepage
diff preview
diff --git a/server/src/html/garden.rs b/server/src/html/garden.rs
index 86b4c184724910bb464682b9025d19ce6cd2050d..82c1b4b4f36ea13a906035a1789ba893222b3695 100644
--- a/server/src/html/garden.rs
+++ b/server/src/html/garden.rs
@@ -1397,10 +1397,14 @@ async fn vote_compare_inner(
}
};
+ let url_key = canonical_view_url(&uri);
+ let view_count = state.views.get_views(&url_key);
+
let page = layout_full_bleed_chromeless(
&title,
"view-ontology view-ontology-light view-vote-compare view-vote-compare-fullscreen",
body,
+ Some(view_count),
theme_from_jar(&jar),
&theme_next_from_uri(&uri),
);
diff --git a/server/src/html/mod.rs b/server/src/html/mod.rs
index f1b0f31b0c516c2e71ca7938d1ec2764e3b077eb..fbc3fc4ed7c53cc68125fb50f5c4db9122f9a261 100644
--- a/server/src/html/mod.rs
+++ b/server/src/html/mod.rs
@@ -310,6 +310,7 @@ pub(super) fn layout_full_bleed_chromeless(
title: &str,
view: &str,
body: Markup,
+ views: Option<u64>,
theme: &str,
theme_next: &str,
) -> Markup {
@@ -317,7 +318,7 @@ pub(super) fn layout_full_bleed_chromeless(
title,
view,
body,
- None,
+ views,
theme,
theme_next,
None,
diff --git a/server/tests/integration.rs b/server/tests/integration.rs
index e96d11c820c3bbaa8bb55ad11636397ef225e55a..62c84862d5c2cba212f9ceabd6273203a43e804c 100644
--- a/server/tests/integration.rs
+++ b/server/tests/integration.rs
@@ -1421,12 +1421,24 @@ async fn test_view_counts_increment_and_display() {
.await
.unwrap();
assert!(v1.status().is_success(), "vote compare GET 1: {}", v1.status());
+ let v1_body = v1.text().await.unwrap();
+ assert!(
+ v1_body.contains("1 views"),
+ "vote compare page should show view count, snippet: {}",
+ v1_body.chars().take(600).collect::<String>()
+ );
let v2 = client
.get(format!("http://{addr}{vote_q_left_first}"))
.send()
.await
.unwrap();
assert!(v2.status().is_success(), "vote compare GET 2: {}", v2.status());
+ let v2_body = v2.text().await.unwrap();
+ assert!(
+ v2_body.contains("2 views"),
+ "vote compare page should reflect incremented count, snippet: {}",
+ v2_body.chars().take(600).collect::<String>()
+ );
assert_eq!(
state.views.get_views(&vote_key),
B — c_efa306aa8040 (tommy-mor)
message
[d1ad77cf] Add transparent HTML evidence graph for epochs and rankings. Persist verbatim commits, comparisons, attempts, and judgments in the ledger, serve them as linkable HTML indexes, and keep ranking resumable across restarts. Co-authored-by: Cursor <cursoragent@cursor.com>
diff preview
diff --git a/constitution.py b/constitution.py
index a58257e1881b21d1d6fa8e68a3faa222e4f661ef..f819007252f435680b8356fb4da83469b21e33af 100644
--- a/constitution.py
+++ b/constitution.py
@@ -156,6 +156,8 @@ DEFAULT_REPOSITORIES = [
"refs": ["refs/heads/**"],
},
]
+PUBLIC_BASE_URL = os.environ.get("PUBLIC_BASE_URL", "https://token.slug.social").rstrip("/")
+EVIDENCE_SCHEMA_VERSION = 2
DEFAULT_CONTRIBUTORS = {
"tommy-mor": ["thmorriss@gmail.com"],
"christopher-whitman": [
@@ -206,6 +208,9 @@ class Emission:
ranking: dict # author -> score str
models_used: list
discovery_snapshot_id: str = "" # empty only for pre-discovery ledger history
+ evidence_schema_version: int = 1
+ ranking_run_id: str = ""
+ ranking_event_id: str = ""
@event
@@ -237,7 +242,299 @@ class GitDiscovery:
commits: list
+@event
+class Evidence:
+ """Versioned, content-addressed constitutional evidence envelope."""
+ schema_version: int
+ event_id: str
+ epoch: int
+ kind: str
+ recorded_at_ms: int
+ previous_event_sha256: str
+ payload: dict
+
+
store = JsonlStore(JSONL_PATH)
+_LEDGER_LOCK = asyncio.Lock()
+
+
+# ===========================================================================
+# §1e. EVIDENCE — content-addressed, append-only, publicly linkable
+# ===========================================================================
+#
+# Every epoch, commit, comparison input, provider attempt, and judgment is
+# recorded as Evidence. Authoritative payloads keep exact bytes as base64 plus
+# SHA-256; decoded text is for display only.
+
+
+def _canonical_json(obj) -> bytes:
+ return json.dumps(obj, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode()
+
+
+def _sha256_hex(data: bytes) -> str:
+ return hashlib.sha256(data).hexdigest()
+
+
+def _bytes_blob(data: bytes | str) -> dict:
+ raw = data.encode("utf-8") if isinstance(data, str) else data
+ return {
+ "encoding": "base64",
+ "data": base64.b64encode(raw).decode("ascii"),
+ "byte_length": len(raw),
+ "sha256": _sha256_hex(raw),
+ "text": raw.decode("utf-8", "replace"),
+ }
+
+
+def _decode_blob(blob: dict | None) -> bytes:
+ if not blob:
+ return b""
+ return base64.b64decode(blob["data"].encode("ascii"))
+
+
+def _content_id(prefix: str, material) -> str:
+ digest = _sha256_hex(_canonical_json(material) if not isinstance(material, bytes) else material)
+ return f"{prefix}_{digest}"
+
+
+def _html_escape(text: str) -> str:
+ return (
+ text.replace("&", "&")
+ .replace("<", "<")
+ .replace(">", ">")
+ .replace('"', """)
+ )
+
+
+def _evidence_path(kind: str, entity_id: str) -> str:
+ routes = {
+ "epoch": f"/epochs/{entity_id}",
+ "commit": f"/commits/{entity_id}",
+ "comparison": f"/comparisons/{entity_id}",
+ "attempt": f"/attempts/{entity_id}",
+ "judgment": f"/judgments/{entity_id}",
+ "event": f"/events/{entity_id}",
+ }
+ return routes[kind]
+
+
+def _evidence_url(kind: str, entity_id: str) -> str:
+ return PUBLIC_BASE_URL + _evidence_path(kind, entity_id)
+
+
+def _previous_event_sha256(events: list) -> str:
+ for event_ in reversed(events):
+ if isinstance(event_, Evidence):
+ return event_.event_id.split("_", 1)[-1]
+ if isinstance(event_, (GitDiscovery, Emission)):
+ return _sha256_hex(_canonical_json(to_dict(event_)))
+ return "0" * 64
+
+
+def _public_repo_row(repo: dict) -> dict:
+ """Strip credential-bearing clone URLs from published config."""
+ url = repo["url"]
+ if "@" in url and "://" in url:
+ scheme, rest = url.split("://", 1)
+ url = f"{scheme}://{rest.split('@', 1)[-1]}"
+ return {"id": repo["id"], "url": url, "refs": repo["refs"]}
+
+
+def _ledger_lock_path() -> pathlib.Path:
+ env = os.environ.get("LEDGER_LOCK_PATH")
+ if env:
+ return pathlib.Path(env)
+ return pathlib.Path(str(store.path) + ".lock")
+
+
+async def append_evidence(epoch: int, kind: str, payload: dict) -> Evidence:
+ """Durably append one Evidence event under process + file locks."""
+ # Logical identity ignores chain links / wall clock so restarts stay idempotent.
+ event_id = _content_id("ev", {
+ "schema_version": EVIDENCE_SCHEMA_VERSION,
+ "epoch": epoch,
+ "kind": kind,
+ "payload": payload,
+ })
+ async with _LEDGER_LOCK:
+ lock_path = _ledger_lock_path()
+ lock_path.parent.mkdir(parents=True, exist_ok=True)
+ with lock_path.open("a+b") as lock_file:
+ fcntl.flock(lock_file.fileno(), fcntl.LOCK_EX)
+ try:
+ events = store.read()
+ existing = next(
+ (
+ e for e in events
+ if isinstance(e, Evidence) and e.event_id == event_id
+ ),
+ None,
+ )
+ if existing:
+ return existing
+ recorded_at_ms = int(time.time() * 1000)
+ previous = _previous_event_sha256(events)
+ evidence = Evidence(
+ schema_version=EVIDENCE_SCHEMA_VERSION,
+ event_id=event_id,
+ epoch=epoch,
+ kind=kind,
+ recorded_at_ms=recorded_at_ms,
+ previous_event_sha256=previous,
+ payload=payload,
+ )
+
+ def append_once(current):
+ if any(
+ isinstance(e, Evidence) and e.event_id == event_id
+ for e in current
+ ):
+ return None
+ return evidence
+
+ appended = await store.atomic(append_once)
+ result = appended or next(
+ e for e in store.read()
+ if isinstance(e, Evidence) and e.event_id == event_id
+ )
+ try:
+ with open(store.path, "rb") as fh:
+ os.fsync(fh.fileno())
+ except OSError:
+ pass
+ finally:
+ fcntl.flock(lock_file.fileno(), fcntl.LOCK_UN)
+ await broadcast_audit(
+ kind,
+ f"{kind}: {payload.get('summary') or event_id[:24]}",
+ phase=PROCESS_STATE.get("phase"),
+ progress=PROCESS_STATE.get("progress"),
+ evidence_event_id=result.event_id,
+ evidence_url=_evidence_url("event", result.event_id),
+ )
+ return result
+
+
+def evidence_by_kind(kind: str | None = None) -> list[Evidence]:
+ rows = [e for e in store.read() if isinstance(e, Evidence)]
+ if kind is None:
+ return rows
+ return [e for e in rows if e.kind == kind]
+
+
+def find_evidence(event_id: str) -> Evidence | None:
+ return next(
+ (e for e in store.read() if isinstance(e, Evidence) and e.event_id == event_id),
+ None,
+ )
+
+
+def find_evidence_payload(kind: str, key: str, value: str) -> Evidence | None:
+ for e in evidence_by_kind(kind):
+ if e.payload.get(key) == value:
+ return e
+ return None
+
+
+def commit_id_for_oid(oid: str) -> str:
+ return _content_id("c", {"oid": oid})
+
+
+def comparison_id_for(material: dict) -> str:
+ return _content_id("cmp", material)
+
+
+def attempt_id_for(comparison_id: str, model_id: str, attempt_number: int) -> str:
+ return _content_id("att", {
+ "comparison_id": comparison_id,
+ "model_id": model_id,
+ "attempt_number": attempt_number,
+ })
+
+
+def judgment_id_for(material: dict) -> str:
+ return _content_id("jud", material)
+
+
+def _blob_text(blob) -> str:
+ if blob is None:
+ return ""
+ if isinstance(blob, str):
+ return blob
+ if isinstance(blob, dict):
+ if isinstance(blob.get("text"), str):
+ return blob["text"]
+ try:
+ return _decode_blob(blob).decode("utf-8", "replace")
+ except Exception:
+ return ""
+ return str(blob)
+
+
+def _discovery_for_epoch(epoch: int) -> GitDiscovery | None:
+ return next(
+ (
+ e for e in store.read()
+ if isinstance(e, GitDiscovery) and e.epoch == epoch
+ ),
+ None,
+ )
+
+
+def _emission_for_epoch(epoch: int) -> Emission | None:
+ return next(
+ (
+ e for e in store.read()
+ if isinstance(e, Emission) and e.epoch == epoch
+ ),
+ None,
+ )
+
+
+def _epochs_in_ledger() -> list[int]:
+ epochs: set[int] = set()
+ for e in store.read():
+ if isinstance(e, (GitDiscovery, Emission, Evidence)):
+ epochs.add(e.epoch)
+ return sorted(epochs)
+
+
+def _legacy_commit_row(commit_id: str) -> tuple[GitDiscovery | None, dict | None]:
+ for discovery in store.read():
+ if not isinstance(discovery, GitDiscovery):
+ continue
+ for commit in discovery.commits:
+ if commit_id_for_oid(commit["oid"]) == commit_id:
+ return discovery, commit
+ return None, None
+
+
+def _legacy_observation(commit_id: str) -> tuple[GitDiscovery | None, dict | None]:
+ for discovery in store.read():
+ if not isinstance(discovery, GitDiscovery):
+ continue
+ for obs in discovery.observations:
+ if commit_id_for_oid(obs["oid"]) == commit_id:
+ return discovery, obs
+ return None, None
+
+
+def build_pairwise_prompt(side_a: dict, side_b: dict) -> str:
+ return f"""You are ranking contributions to an open source project.
+Compare these two sides (each may be one or more commits). Decide which side contributed more.
+Return ONLY a JSON object: {{"winner": "A" or "B", "ratio": "N:M", "explanation": "..."}}
+
+Side A — commit messages:
+{side_a['message']}
+
+Side A — unified diffs (full patches):
+{side_a['diff']}
+
+Side B — commit messages:
+{side_b['message']}
+
+Side B — unified diffs (full patches):
+{side_b['diff']}"""
# ===========================================================================
@@ -565,45 +862,126 @@ async def fetch_top_models(n=3):
def _retry_llm_pairwise(exc: BaseException) -> bool:
- if isinstance(exc, (json.JSONDecodeError, KeyError, IndexError, TypeError)):
+ if isinstance(exc, (json.JSONDecodeError, KeyError, IndexError, TypeError, ValueError)):
return True
if isinstance(exc, httpx.HTTPStatusError):
return exc.response.status_code in (408, 425, 429, 500, 502, 503, 504)
return isinstance(exc, httpx.RequestError)
-@retry(
- retry=retry_if_exception(_retry_llm_pairwise),
- stop=stop_after_attempt(6),
- wait=wait_exponential(multiplier=1, min=1, max=120),
- reraise=True,
-)
-async def llm_pairwise_compare(model_id, side_a, side_b):
- prompt = f"""You are ranking contributions to an open source project.
-Compare these two sides (each may be one or more commits). Decide which side contributed more.
-Return ONLY a JSON object: {{"winner": "A" or "B", "ratio": "N:M", "explanation": "..."}}
+def _retry_wait_seconds(attempt_number: int) -> float:
+ return min(120.0, float(2 ** (attempt_number - 1)))
-Side A — commit messages:
-{side_a['message']}
-Side A — unified diffs (full patches):
-{side_a['diff']}
-
-Side B — commit messages:
-{side_b['message']}
-
-Side B — unified diffs (full patches):
-{side_b['diff']}"""
-
- async with httpx.AsyncClient(timeout=httpx.Timeout(120.0, connect=30.0)) as client:
- resp = await client.post(
- f"{OPENROUTER_BASE_URL}/api/v1/chat/completions",
- headers={"Authorization": f"Bearer {OPENROUTER_API_KEY}"},
- json={"model": model_id, "messages": [{"role": "user", "content": prompt}]},
+async def llm_pairwise_compar
… preview truncated; 68,950 characters omittedHardlinks — judgments / attempts / prompt
judgments
attempts
Prompt text is loaded only by the download route.