diff --git a/.claude/board/entries/2026-09-25-lance12-lancedb039-sweep.md b/.claude/board/entries/2026-09-25-lance12-lancedb039-sweep.md index 0548e0a60..6237f731f 100644 --- a/.claude/board/entries/2026-09-25-lance12-lancedb039-sweep.md +++ b/.claude/board/entries/2026-09-25-lance12-lancedb039-sweep.md @@ -5,7 +5,7 @@ ## What moved - `lance*` `11.*` → `12.*` and `lancedb` `0.38.*` → `0.39.*`, measured against crates.io. lancedb 0.39.0 pins `lance = "=12.0.0"`. arrow ^58 and datafusion ^54 are unmoved. - **`object_store` 0.13 → 0.14 for our direct dependency.** lance 12 requires ^0.14.1, while datafusion 54 stays on ^0.13.2, so both majors are in the graph (upstream's split). Every direct call site here hands the store or a `Path` to lance or to lance-graph-hydrate. On 0.13, `memwal_atomicity_probe` and `hydration_probe` failed with E0308 ("multiple different versions of crate `object_store`"). -- **lancedb `remote` on the consumers behind `lancedb-sdk`** (lance-graph, surreal_container). 0.39.0 does not compile without it: `pub mod job;` is ungated while `job.rs` uses the `remote`-gated `Error::Http`. It is set at crate level, so it is active only with the optional dependency. +- **lancedb `remote` on the consumers behind `lancedb-sdk`** (lance-graph, surreal_container). 0.39.0 does not compile without it: `pub mod job;` is ungated while `job.rs` uses the `remote`-gated `Error::Http`. It is set at crate level, so it is active only with the optional dependency. Measured cost: +10 crates (630 vs 620). reqwest, tonic, prost and http were already present via lance; the delta is arrow-flight, axum 0.7 and 0.8 (two majors), axum-core ×2, matchit ×2, tower-http, serde_path_to_error and httpdate. - Sub-crate toolchains (`reader-lm`, `bge-m3`, `python`) 1.95.0 → 1.98.1, matching the root. Builder images for `cognitive-stack`, `symbiont` and `thinking-engine` (was 1.82) → `rust:1.98`. ## Gates diff --git a/crates/lance-graph-quack/src/lib.rs b/crates/lance-graph-quack/src/lib.rs index 7c267f886..87f7b6cff 100644 --- a/crates/lance-graph-quack/src/lib.rs +++ b/crates/lance-graph-quack/src/lib.rs @@ -1244,13 +1244,18 @@ pub struct GroupAvgPlan { /// /// # Errors /// -/// As [`lower`]. +/// [`LowerError::EmptyGroupUniverse`] when `groups == 0`, for every key kind: +/// the count half is a `GroupReduce`, whose sink must be a non-empty +/// `Out::I64`, so a K = 0 plan could never execute. Otherwise as [`lower`]. pub fn lower_group_avg( filter: &Filter, key: GroupAddr, val: Col, groups: u32, ) -> Result { + if groups == 0 { + return Err(LowerError::EmptyGroupUniverse); + } let sum_agg = match key { GroupAddr::Local(k) => Agg::GroupSumI32 { key: k, val }, GroupAddr::Via { fk, key } => Agg::GroupSumViaI32 { fk, key, val }, @@ -2131,6 +2136,30 @@ mod tests { /// FAILS IF a zero-group HAVING lowers to programs the executor would /// refuse, instead of being refused at lowering with a named error. + #[test] + fn lower_group_avg_refuses_an_empty_group_universe_for_every_key_kind() { + // The count half is `GroupReduce Count` for every key kind, and that + // terminal needs a non-empty `Out::I64`: a K = 0 plan could never run. + let filter = Filter::cmp(Col(0), Cmp::EqU32(1)); + let keys = [ + GroupAddr::Local(Col(1)), + GroupAddr::Pair { + hi: Col(1), + lo: Col(2), + stride: 3, + }, + ]; + for key in keys { + assert_eq!( + lower_group_avg(&filter, key, Col(3), 0).map(|_| ()), + Err(LowerError::EmptyGroupUniverse), + "{key:?}" + ); + // One group lowers: the refusal is about K alone. + assert!(lower_group_avg(&filter, key, Col(3), 1).is_ok(), "{key:?}"); + } + } + #[test] fn lower_group_having_refuses_an_empty_group_universe() { let q = GroupHaving { diff --git a/crates/lance-graph/Cargo.toml b/crates/lance-graph/Cargo.toml index 5eee645a1..ca031608e 100644 --- a/crates/lance-graph/Cargo.toml +++ b/crates/lance-graph/Cargo.toml @@ -57,6 +57,11 @@ lance-namespace = "12.*" # (E0599). Same upstream defect as 0.38.0; no 0.39 patch release fixes it. # Declared here, not in the workspace entry, so it is active only when # `lancedb-sdk` turns the optional dep on. Drop it once upstream gates `job`. +# Cost, measured with `cargo tree -p lance-graph --features lancedb-sdk` +# (2026-09-25): +10 crates (630 vs 620). reqwest / tonic / prost / http are +# already in the graph via lance; the delta is arrow-flight, axum 0.7 + 0.8 +# (two majors), axum-core x2, matchit x2, tower-http, serde_path_to_error, +# httpdate. lancedb = { workspace = true, optional = true, features = ["remote"] } nom = "7.1" serde = { version = "1", features = ["derive"] }