Side A ships a coherent, working feature (Reddit OAuth linking, UUID-canonical identity, trust-weight logic, privacy-preserving account page) with accompanying tests and mock server updates, all real project logic. Side B is dominated by vendoring an entire unrelated third-party 'durable' crate (with its own README, RFC docs, examples, licenses, and a huge generated Cargo.lock) plus a modest, legitimate refactor (entity payloads to RocksDB, streaming event replay); the actual server-side diff is reasonable but is buried in enormous non-project noise inflating the patch without proportional lasting value. A's diff is entirely purposeful application code and tests, giving cleaner, more reviewable ownership value than B's mix of a real improvement plus vendored boilerplate.
constitution · epochs · watch · epoch 3
c_afa638171cf7 (tommy-mor) vs c_2dc96aace098 (tommy-mor)
download prompt · raw event · cmp_5eba8ad0824fd8
council reasoning
B’s lasting value is the architectural shift that keeps full Reddit entity JSON out of the in-memory GlobalTree (new EntityStore on RocksDB via workspace durable) and replaces load_all with line-at-a-time event_log.replay, which directly cuts startup RAM and scales the core data path. A is strong product work (Reddit OAuth, UUID-canonical linking, private linked-providers UI and trust-weight/batch fixes), but it extends auth rather than changing how the system stores and reconstitutes principal data; B’s huge line count is largely the vendored durable crate, yet the server-facing storage/replay change is the more foundational, durable improvement.
Side A delivers a substantial architectural change to identity management: it makes UUIDs the canonical identity, refactors OAuth into a provider-agnostic linking flow, adds Reddit OAuth, prevents linking conflicts, exposes linked providers privately, and fixes trust-weight updates during projection replay with tests. Side B includes a useful startup-memory optimization by streaming event-log replay and moves entity payloads into a RocksDB-backed store, but much of the patch is vendoring a new `durable` crate, dependency churn, examples, and documentation whose lasting value is less directly tied to the application's core behavior.
sides
A — c_afa638171cf7 (tommy-mor)
message
[52f5c51c] Add Reddit OAuth linking and make UUID the only account identity. OAuth providers only attach to a session UUID (first link creates the principal); linked providers stay private on the account page. Co-authored-by: Cursor <cursoragent@cursor.com>
diff preview
diff --git a/AGENTS.md b/AGENTS.md
index 6e0fd8ebb65d665c9c1438e3275971d62b98fd95..e9cc3173dbeb21ad0fc090ca7b407b027c7820a9 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -35,8 +35,11 @@ Environment variables (defaults in `server/src/state.rs`):
- `SORTER2_DATA_DIR` — default `./data` (created on startup)
- `SORTER2_EVENT_LOG` — default `{data_dir}/events.jsonl`
- `SORTER2_BASE_URL` — public origin (also drives Secure cookies when `https://`)
-- `GITHUB_CLIENT_ID` / `GITHUB_CLIENT_SECRET` — GitHub OAuth (optional; login disabled if unset)
-- `SORTER2_ALLOW_MOCK_OAUTH=1` — allow `mock_user` on `/auth/github` (tests only)
+- `GITHUB_CLIENT_ID` / `GITHUB_CLIENT_SECRET` — GitHub OAuth linking (optional)
+- `REDDIT_CLIENT_ID` / `REDDIT_CLIENT_SECRET` (or `REDDIT_APP_*`) — Reddit API import + OAuth linking (optional)
+- `SORTER2_ALLOW_MOCK_OAUTH=1` — allow `mock_user` on `/auth/github` and `/auth/reddit` (tests only)
+
+Identity: UUID is canonical. OAuth providers only *link* to a UUID (first link creates the principal). Linked providers are private to the account owner.
Health check: `GET /healthz` → `ok`.
diff --git a/server/src/auth/mod.rs b/server/src/auth/mod.rs
index 5906f93b13853421e96a3c37bc9d8202a47842bf..c706ae8045a811e5941f5f6c72da88f42a403a82 100644
--- a/server/src/auth/mod.rs
+++ b/server/src/auth/mod.rs
@@ -1,4 +1,8 @@
-//! GitHub OAuth login, session cookies, and vote actor resolution.
+//! OAuth linking, session cookies, and vote actor resolution.
+//!
+//! Canonical identity is a UUID. OAuth providers only *link* to that UUID
+//! (first link creates the principal; later links attach while logged in).
+//! Which providers are linked is private to the account owner.
pub mod config;
pub mod identity;
@@ -22,7 +26,9 @@ use crate::{
form_template::template_json_compact,
html::layout,
state::AppState,
- storage_schema::{oauth_link_owner, pseudonym_owner, Store, StoreFields},
+ storage_schema::{
+ linked_providers_for_uuid, oauth_link_owner, pseudonym_owner, Store, StoreFields,
+ },
ui_action::UI_RPC_FIELD,
};
@@ -53,10 +59,12 @@ fn new_actor_uuid() -> String {
pub struct LoginQuery {
#[serde(default)]
pub return_to: Option<String>,
+ #[serde(default)]
+ pub error: Option<String>,
}
#[derive(Debug, Deserialize)]
-pub struct GitHubStartQuery {
+pub struct OAuthStartQuery {
#[serde(default)]
pub return_to: Option<String>,
#[serde(default)]
@@ -72,15 +80,22 @@ fn return_from_query_or_jar(jar: &CookieJar, query: Option<&str>) -> String {
.unwrap_or_else(|| "/".to_string())
}
-fn oauth_providers(base_url: &str, return_to: &str) -> Vec<(&'static str, String)> {
+/// Available OAuth link targets: `(provider_key, label, start_href)`.
+fn oauth_providers(base_url: &str, return_to: &str) -> Vec<(&'static str, &'static str, String)> {
let mut out = Vec::new();
+ let enc = urlencoding::encode(return_to);
if oauth::GitHubConfig::from_env(base_url).is_some() {
out.push((
- "GitHub",
- format!(
- "/auth/github?return_to={}",
- urlencoding::encode(return_to)
- ),
+ "github",
+ oauth::provider_label("github"),
+ format!("/auth/github?return_to={enc}"),
+ ));
+ }
+ if oauth::RedditConfig::from_env(base_url).is_some() {
+ out.push((
+ "reddit",
+ oauth::provider_label("reddit"),
+ format!("/auth/reddit?return_to={enc}"),
));
}
out
@@ -125,23 +140,41 @@ fn alias_claim_forms(return_to: &str, submit_label: &str) -> Result<Markup, Stat
})
}
-fn signed_out_body(providers: &[(&str, String)]) -> Markup {
+fn login_error_message(code: Option<&str>) -> Option<&'static str> {
+ match code {
+ Some("oauth_taken") => {
+ Some("that OAuth account is already linked to a different sorter2 account")
+ }
+ Some("oauth_failed") => Some("OAuth failed — try again"),
+ _ => None,
+ }
+}
+
+fn signed_out_body(
+ providers: &[(&str, &str, String)],
+ error: Option<&str>,
+) -> Markup {
html! {
main class="panel login-page" {
section class="login-section" {
h1 { "sign in" }
- p class="muted" { "link an account to vote under a lasting alias" }
+ p class="muted" {
+ "link an OAuth account to create your identity, then claim an alias to vote"
+ }
+ @if let Some(msg) = login_error_message(error) {
+ p class="alias-bad" data-testid="login-error" { (msg) }
+ }
@if providers.is_empty() {
p class="muted" {
- "OAuth is not configured. Set GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET."
+ "OAuth is not configured. Set GitHub and/or Reddit client credentials."
}
} @else {
ul class="oauth-provider-list" {
- @for (name, href) in providers {
+ @for (key, label, href) in providers {
li {
a href=(href) class="btn-primary oauth-provider"
- data-testid=(format!("oauth-{}", name.to_lowercase())) {
- (format!("Continue with {name}"))
+ data-testid=(format!("oauth-{key}")) {
+ (format!("Link {label}"))
}
}
}
@@ -156,7 +189,10 @@ fn signed_out_body(providers: &[(&str, String)]) -> Markup {
fn account_body(
actor: &session::SessionActor,
aliases: &[String],
- providers: &[(&str, String)],
+ // Provider keys already linked to this UUID (private).
+ linked: &[String],
+ // Providers available to link: not yet attached.
+ unlinkable: &[(&str, &str, String)],
claim_forms: Markup,
) -> Markup {
let current = actor.pseudonym.trim();
@@ -212,16 +248,29 @@ fn account_body(
(claim_forms)
}
- @if !providers.is_empty() {
- section class="login-section" {
- h2 { "linked sign-in" }
- p class="muted small" { "sign in again with the same provider to return to this account" }
+ section class="login-section" {
+ h2 { "linked sign-in" }
+ p class="muted small" {
+ "private to you — linking more providers raises trust weight without publishing which accounts you use"
+ }
+ @if linked.is_empty() {
+ p class="muted" data-testid="linked-providers-empty" { "none yet" }
+ } @else {
+ ul class="linked-provider-list" data-testid="linked-providers" {
+ @for key in linked {
+ li data-testid=(format!("linked-{key}")) {
+ (oauth::provider_label(key))
+ }
+ }
+ }
+ }
+ @if !unlinkable.is_empty() {
ul class="oauth-provider-list" {
- @for (name, href) in providers {
+ @for (key, label, href) in unlinkable {
li {
a href=(href) class="btn-secondary oauth-provider"
- data-testid=(format!("oauth-relink-{}", name.to_lowercase())) {
- (format!("Re-link {name}"))
+ data-testid=(format!("oauth-link-{key}")) {
+ (format!("Link {label}"))
}
}
}
@@ -243,12 +292,21 @@ fn account_body(
fn login_body(
session: Option<&session::SessionActor>,
aliases: &[String],
- providers: &[(&str, String)],
+ linked: &[String],
+ providers: &[(&str, &str, String)],
claim_forms: Option<Markup>,
+ error: Option<&str>,
) -> Markup {
match (session, claim_forms) {
- (Some(actor), Some(forms)) => account_body(actor, aliases, providers, forms),
- _ => signed_out_body(providers),
+ (Some(actor), Some(forms)) => {
+ let unlinkable: Vec<_> = providers
+ .iter()
+ .filter(|(key, _, _)| !linked.iter().any(|p| p == key))
+ .cloned()
+ .collect();
+ account_body(actor, aliases, linked, &unlinkable, forms)
+ }
+ _ => signed_out_body(providers, error),
}
}
@@ -268,6 +326,10 @@ pub async fn login_page(
.as_ref()
.map(|s| alias_list(db, &s.uuid))
.unwrap_or_default();
+ let linked = session
+ .as_ref()
+ .map(|s| linked_providers_for_uuid(db, &s.uuid).unwrap_or_default())
+ .unwrap_or_default();
let providers = oauth_providers(&base_url_from_env(state.cfg.port), &return_to);
let claim_forms = if session.is_some() {
@@ -282,7 +344,14 @@ pub async fn login_page(
} else {
"login · sorter2"
},
- login_body(session.as_ref(), &aliases, &providers, claim_forms),
+ login_body(
+ session.as_ref(),
+ &aliases,
+ &linked,
+ &providers,
+ claim_forms,
+ query.error.as_deref(),
+ ),
state.views.get_views("/login"),
session
.as_ref()
@@ -302,7 +371,6 @@ pub async fn alias_page(
let db = state.projection_store.db();
let session = session::load_valid_session(db, &session_id).ok_or(StatusCode::UNAUTHORIZED)?;
if session::session_has_pseudonym(&session) {
- // Already onboarded — manage aliases on the account page.
return Ok(Redirect::to("/login").into_response());
}
@@ -331,7 +399,7 @@ pub async fn alias_page(
pub async fn github_start(
State(state): State<AppState>,
jar: CookieJar,
- Query(query): Query<GitHubStartQuery>,
+ Query(query): Query<OAuthStartQuery>,
) -> Result<Response, StatusCode> {
let cfg = oauth::GitHubConfig::from_env(&base_url_from_env(state.cfg.port))
.ok_or(StatusCode::SERVICE_UNAVAILABLE)?;
@@ -342,7 +410,28 @@ pub async fn github_start(
} else {
None
};
- let url = oauth::authorize_url(&cfg, &state_token, mock_user);
+ let url = oauth::github_authorize_url(&cfg, &state_token, mock_user);
+ let jar = jar
+ .add(session::oauth_state_cookie_value(&state_token))
+ .add(session::auth_return_cookie_value(&return_to));
+ Ok((jar, Redirect::temporary(&url)).into_response())
+}
+
+pub async fn reddit_start(
+ State(state): State<AppState>,
+ jar: CookieJar,
+ Query(query): Query<OAuthStartQuery>,
+) -> Result<Response, StatusCode> {
+ let cfg = oauth::RedditConfig::from_env(&base_url_from_env(state.cfg.port))
+ .ok_or(StatusCode::SERVICE_UNAVAILABLE)?;
+ let return_to = return_from_query_or_jar(&jar, query.return_to.as_deref());
+ let state_token = session::new_oauth_state();
+ let mock_user = if config::mock_oauth_allowed() {
+ query.mock_user.as_deref()
+ } else {
+ None
+ };
+ let url = oauth::reddit_authorize_url(&cfg, &state_token, mock_user);
let jar = jar
.add(session::oauth_state_cookie_value(&state_token))
.add(session::auth_return_cookie_value(&return_to));
@@ -355,6 +444,13 @@ pub struct OAuthCallbackQuery {
pub state: String,
}
+/// Link `provider:provider_id` to a UUID.
+///
+/// - Logged in + new provider → attach to session UUID
+/// - Logged in + already ours → no-op
+/// - Logged in
… preview truncated; 29,823 characters omittedB — c_2dc96aace098 (tommy-mor)
message
[285b64d4] Offload Reddit payloads to RocksDB and stream event log replay. Vendor durable as a workspace crate, store entity JSON in entity_db instead of GlobalTree, and replay events.jsonl one line at a time to cut startup RAM. Co-authored-by: Cursor <cursoragent@cursor.com>
diff preview
diff --git a/Cargo.lock b/Cargo.lock index e55d87f32ab32064686431c7082ef8c9ca872d63..8fc09f9ac978bd7ccf57f177989057b606677db8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,18 @@ dependencies = [ "memchr", ] +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + [[package]] name = "anyhow" version = "1.0.102" @@ -56,6 +68,12 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + [[package]] name = "axum" version = "0.7.9" @@ -153,6 +171,75 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bindgen" +version = "0.65.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" +dependencies = [ + "bitflags 1.3.2", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 1.1.0", + "shlex", + "syn", +] + +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags 2.11.1", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "proc-macro2", + "quote", + "regex", + "rustc-hash 2.1.2", + "shlex", + "syn", +] + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.11.1" @@ -171,6 +258,22 @@ version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +[[package]] +name = "bzip2-sys" +version = "0.1.13+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" +dependencies = [ + "cc", + "pkg-config", +] + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + [[package]] name = "cc" version = "1.2.62" @@ -178,15 +281,89 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" dependencies = [ "find-msvc-tools", + "jobserver", + "libc", "shlex", ] +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + [[package]] name = "cookie" version = "0.18.1" @@ -224,6 +401,73 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools 0.10.5", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools 0.10.5", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + [[package]] name = "deranged" version = "0.5.8" @@ -250,6 +494,25 @@ version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" +[[package]] +name = "durable" +version = "0.1.0" +dependencies = [ + "bincode", + "criterion", + "proptest", + "rocksdb", + "serde", + "tempfile", + "thiserror", +] + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + [[package]] name = "encoding_rs" version = "0.8.35" @@ -373,6 +636,18 @@ dependencies = [ "wasi", ] +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi 5.3.0", + "wasip2", +] + [[package]] name = "getrandom" version = "0.4.2" @@ -381,11 +656,17 @@ checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", "libc", - "r-efi", + "r-efi 6.0.0", "wasip2", "wasip3", ] +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + [[package]] name = "h2" version = "0.4.14" @@ -405,6 +686,17 @@ dependencies = [ "tracing", ] +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + [[package]] name = "hashbrown" version = "0.15.5" @@ -426,6 +718,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + [[package]] name = "http" version = "1.4.1" @@ -676,12 +974,51 @@ version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" +[[package]] +name = "is-terminal" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + [[package]] name = "js-sys" version = "0.3.99" @@ -700,6 +1037,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "leb128fmt" version = "0.1.0" @@ -712,6 + … preview truncated; 197,635 characters omitted
Hardlinks — judgments / attempts / prompt
judgments
attempts
Prompt text is loaded only by the download route.