From 090bd00c9798cf9a32d23af6a534eee6a027749e Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 22 Aug 2026 13:18:34 +0000 Subject: [PATCH 1/8] ci: every job builds on the pinned toolchain, not on `stable` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All seven workflows installed `stable` and set it as the rustup default. For the jobs that run cargo inside the repo that was merely misleading — `rust-toolchain.toml` (channel = 1.97.1, components rustfmt + clippy) wins over `rustup default`, so they already ran on the pin while the YAML said otherwise, and a `matrix.toolchain: [stable]` that nothing else referenced looked like a version axis while selecting nothing. For two jobs it was not merely misleading. `release.yml` and `rust-publish.yml` pass `toolchain: stable` to `actions-rust-lang/setup-rust-toolchain@v1`, which pins the environment to that input — so the SHIPPED artifact and the published crates were built with whatever `stable` was that week, not with the channel this repo pins. The fix deliberately does NOT restate `1.97.1` in seven files. `rust-toolchain.toml` says why in its own comment — "a bump edits `channel` and leaves the prose behind" — and seven restatements are seven places for that to happen. Instead: - `rustup toolchain install stable && rustup default stable` (and the matrix variant) → `rustup show`, which installs and activates whatever the file pins, including its components. - the two action-based setups → drop the `toolchain:` input so the action reads `rust-toolchain.toml`. That is the shape `style.yml:146` already uses for its rustfmt job, so this follows existing practice here rather than importing a convention. Result: one file names the version; every job follows it; a bump stays one edit. Verified locally: all seven parse under `yaml.safe_load`, and no `toolchain: stable` / `rustup default stable` / `toolchain install stable` remains anywhere under `.github/workflows/`. NOT verified locally, and it cannot be: whether the runner behaves as intended. GitHub Actions does not run in this sandbox, so the first real evidence is the first CI run on this branch — the `rustup show` step prints the selected toolchain, which is what to read there. --- .github/workflows/build.yml | 17 +++++++++-------- .github/workflows/jc-proof.yml | 4 ++-- .github/workflows/release.yml | 4 +++- .github/workflows/rust-publish.yml | 4 +++- .github/workflows/rust-test.yml | 30 ++++++++++++++++++------------ .github/workflows/style.yml | 13 +++++++++---- .github/workflows/weather-poc.yml | 4 ++-- 7 files changed, 46 insertions(+), 30 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cb628d311..15fb96a47 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,10 +39,6 @@ jobs: defaults: run: working-directory: lance-graph - strategy: - matrix: - toolchain: - - stable steps: - uses: actions/checkout@v4 with: @@ -56,10 +52,15 @@ jobs: with: repository: AdaWorldAPI/OGAR path: OGAR - - name: Setup rust toolchain - run: | - rustup toolchain install ${{ matrix.toolchain }} - rustup default ${{ matrix.toolchain }} + # Toolchain comes from `rust-toolchain.toml` (channel + components), NOT + # from a version restated here. `rustup show` installs and activates + # whatever that file pins — so a bump is ONE edit in ONE file, which is + # exactly what that file's own comment asks for ("a bump edits `channel` + # and leaves the prose behind"). Previously this installed `stable` and + # set it as default; inside the repo the toolchain file won anyway, so + # these jobs already ran on the pin while the workflow said otherwise. + - name: Setup rust toolchain (pinned by rust-toolchain.toml) + run: rustup show - name: Setup mold linker # Parity with rust-test.yml: the heavy lance+datafusion build + test # binaries hit the GNU-ld/rust-lld RSS+disk cliff at the link step diff --git a/.github/workflows/jc-proof.yml b/.github/workflows/jc-proof.yml index fd8696898..abb4cc7ba 100644 --- a/.github/workflows/jc-proof.yml +++ b/.github/workflows/jc-proof.yml @@ -32,8 +32,8 @@ jobs: run: git clone --depth 1 https://github.com/AdaWorldAPI/ndarray ../ndarray - name: Setup Rust run: | - rustup toolchain install stable - rustup default stable + # Pinned by rust-toolchain.toml — never a version restated here. + rustup show - name: Run JC tests (6 unit tests) run: cargo test --manifest-path crates/jc/Cargo.toml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5f43c6948..c0c4b3fd6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -68,7 +68,9 @@ jobs: - uses: actions-rust-lang/setup-rust-toolchain@v1 with: - toolchain: stable + # No `toolchain:` input — the action reads `rust-toolchain.toml`, so the + # SHIPPED artifact is built with the pinned channel rather than whatever + # `stable` is that week. Same shape style.yml already uses for rustfmt. components: rustfmt, clippy cache: false diff --git a/.github/workflows/rust-publish.yml b/.github/workflows/rust-publish.yml index ded7ddc84..157a61570 100644 --- a/.github/workflows/rust-publish.yml +++ b/.github/workflows/rust-publish.yml @@ -41,7 +41,9 @@ jobs: - uses: actions-rust-lang/setup-rust-toolchain@v1 with: - toolchain: stable + # No `toolchain:` input — the action reads `rust-toolchain.toml`, so the + # SHIPPED artifact is built with the pinned channel rather than whatever + # `stable` is that week. Same shape style.yml already uses for rustfmt. components: rustfmt, clippy cache: false diff --git a/.github/workflows/rust-test.yml b/.github/workflows/rust-test.yml index 5bc4ee160..eaf8608c5 100644 --- a/.github/workflows/rust-test.yml +++ b/.github/workflows/rust-test.yml @@ -48,10 +48,6 @@ jobs: defaults: run: working-directory: lance-graph - strategy: - matrix: - toolchain: - - stable steps: - uses: actions/checkout@v4 with: @@ -69,10 +65,15 @@ jobs: with: repository: AdaWorldAPI/OGAR path: OGAR - - name: Setup rust toolchain - run: | - rustup toolchain install ${{ matrix.toolchain }} - rustup default ${{ matrix.toolchain }} + # Toolchain comes from `rust-toolchain.toml` (channel + components), NOT + # from a version restated here. `rustup show` installs and activates + # whatever that file pins — so a bump is ONE edit in ONE file, which is + # exactly what that file's own comment asks for ("a bump edits `channel` + # and leaves the prose behind"). Previously this installed `stable` and + # set it as default; inside the repo the toolchain file won anyway, so + # these jobs already ran on the pin while the workflow said otherwise. + - name: Setup rust toolchain (pinned by rust-toolchain.toml) + run: rustup show - name: Setup mold linker # Heavy lance+datafusion integration-test binaries OOM the default GNU `ld` # at the `cargo test --no-run` link step (intermittent). mold links them @@ -258,10 +259,15 @@ jobs: with: repository: AdaWorldAPI/OGAR path: OGAR - - name: Setup rust toolchain - run: | - rustup toolchain install stable - rustup default stable + # Toolchain comes from `rust-toolchain.toml` (channel + components), NOT + # from a version restated here. `rustup show` installs and activates + # whatever that file pins — so a bump is ONE edit in ONE file, which is + # exactly what that file's own comment asks for ("a bump edits `channel` + # and leaves the prose behind"). Previously this installed `stable` and + # set it as default; inside the repo the toolchain file won anyway, so + # these jobs already ran on the pin while the workflow said otherwise. + - name: Setup rust toolchain (pinned by rust-toolchain.toml) + run: rustup show - name: Setup mold linker # Parity with the `test` job above (TD-CI-COVERAGE-MOLD-1): the heavy # lance+datafusion test binaries OOM the default GNU `ld` at link diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml index 7e076072a..26e1486fa 100644 --- a/.github/workflows/style.yml +++ b/.github/workflows/style.yml @@ -54,10 +54,15 @@ jobs: with: repository: AdaWorldAPI/OGAR path: OGAR - - name: Setup rust toolchain - run: | - rustup toolchain install stable - rustup default stable + # Toolchain comes from `rust-toolchain.toml` (channel + components), NOT + # from a version restated here. `rustup show` installs and activates + # whatever that file pins — so a bump is ONE edit in ONE file, which is + # exactly what that file's own comment asks for ("a bump edits `channel` + # and leaves the prose behind"). Previously this installed `stable` and + # set it as default; inside the repo the toolchain file won anyway, so + # these jobs already ran on the pin while the workflow said otherwise. + - name: Setup rust toolchain (pinned by rust-toolchain.toml) + run: rustup show rustup component add clippy - uses: Swatinem/rust-cache@v2 with: diff --git a/.github/workflows/weather-poc.yml b/.github/workflows/weather-poc.yml index bee95ff03..15d139dca 100644 --- a/.github/workflows/weather-poc.yml +++ b/.github/workflows/weather-poc.yml @@ -33,8 +33,8 @@ jobs: persist-credentials: false - name: Setup Rust run: | - rustup toolchain install stable - rustup default stable + # Pinned by rust-toolchain.toml — never a version restated here. + rustup show - name: Test zero-dependency codec path run: cargo test --manifest-path crates/weather-poc/Cargo.toml - name: Test live canonical NodeRow agreement path From 16e9929307aad2d67e23eedbd562b33631698732 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 22 Aug 2026 13:59:24 +0000 Subject: [PATCH 2/8] =?UTF-8?q?ci:=20gate=20every=20workspace=20member=20?= =?UTF-8?q?=E2=80=94=20eleven=20were=20reached=20by=20no=20job=20at=20all?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up to the toolchain commit on this branch, from the same measurement. No workflow here runs `--workspace` or `--all`; every gate names one crate by path. So `[workspace] members` gates nothing, and ELEVEN of 25 members were in no job. Two of them (catalog, planner) are deps of lance-graph, so their libs compiled inside a gated build while their tests ran nowhere; the rest compiled nowhere at all. That is how `lance-graph-hydrate` reached `main` un-compiling — `object_store 0.13.2`, a semver-compatible release already in the lockfile, moved `get`/`put` onto `ObjectStoreExt`, and nothing could see it. Every step added here was RUN LOCALLY on the pinned 1.97.1 first, and the count in each comment is what it returned. Nothing is armed on hope: catalog 12+15 · planner 368+4 · ontology 276+2+6 · rbac 23 · archetype 16 consumer-conformance 8 · sigma-tier-router 20 · neural-debug 11 cognitive-shader-driver 107+2 · hydrate 33 `lance-graph-benches` gets `cargo check --benches` instead of `cargo test`: it is a benches-only crate with one `harness = false` target, so `cargo test` runs zero tests and would be a gate that cannot fail. Same shape build.yml already uses for lance-graph's own benches. Formatting gates for the same ten. Nine were already rustfmt-clean and simply had nothing holding them there; `sigma-tier-router` was NOT — 14 hunks in one file, formatted here, rustfmt output only, its 20 tests unchanged. Two things this deliberately does NOT do: - No clippy gates for these crates. Not measured crate-by-crate, and `causal-edge` alone already carries 7 pre-existing findings (ISS-CAUSAL-EDGE-CARRIES-SEVEN-PRE-EXISTING-CLIPPY-FINDINGS). Arming a lint gate on unmeasured crates fails PRs for defects they did not introduce. - No `--workspace` job. That would catch every FUTURE member automatically and is the better long-run answer, but it is a scope and cost decision, not a repair. This closes the measured gap; the standing choice stays open. Also here, because a PR that adds a gate must leave CI green: the two-line `ObjectStoreExt` import that makes `lance-graph-hydrate` compile, its rustfmt pass (4 files), and its live `trim_split_whitespace` clippy warning. Those same fixes ride with the crate's own PR (claude/hydrate-from-zip) — identical hunks, so a rebase after this merges drops them cleanly. Without them the hydrate gate added here would be red on arrival. Cargo.lock is NOT in this commit. It gained one line (`blake3`) from advancing the OGAR sibling checkout #265 → #279, which is a deliberate dependency bump and not part of a CI change. Verified: all seven workflows parse under `yaml.safe_load`, and every one of the 25 workspace members now resolves to at least one job. Background: EPIPHANIES E-THE-GATE-IS-A-HAND-MAINTAINED-ALLOWLIST-NOT-THE-WORKSPACE-1 and ISSUES ISS-CI-GATE-IS-AN-ALLOWLIST-NINE-MEMBERS-UNGATED, both landing with claude/hydrate-from-zip. --- .github/workflows/rust-test.yml | 46 ++++++++++++ .github/workflows/style.yml | 32 +++++++++ crates/lance-graph-hydrate/src/copy.rs | 16 ++++- crates/lance-graph-hydrate/src/dirty.rs | 10 ++- crates/lance-graph-hydrate/src/file.rs | 16 ++++- crates/lance-graph-hydrate/src/marker.rs | 2 +- crates/lance-graph-hydrate/src/publish.rs | 5 +- crates/sigma-tier-router/src/lib.rs | 88 ++++++++++++++++------- 8 files changed, 177 insertions(+), 38 deletions(-) diff --git a/.github/workflows/rust-test.yml b/.github/workflows/rust-test.yml index eaf8608c5..8acef66d8 100644 --- a/.github/workflows/rust-test.yml +++ b/.github/workflows/rust-test.yml @@ -184,6 +184,52 @@ jobs: # ISS-CAUSAL-EDGE-CARRIES-SEVEN-PRE-EXISTING-CLIPPY-FINDINGS instead. - name: Run causal-edge tests (workspace-excluded, previously ungated) run: cargo test --manifest-path crates/causal-edge/Cargo.toml + # ── The rest of the allowlist gap, closed in one pass ──────────────── + # + # No workflow here runs `--workspace`, so `[workspace] members` gates + # nothing and ELEVEN of 25 members were reached by no job at all + # (EPIPHANIES E-THE-GATE-IS-A-HAND-MAINTAINED-ALLOWLIST-NOT-THE-WORKSPACE-1, + # ISSUES ISS-CI-GATE-IS-AN-ALLOWLIST-NINE-MEMBERS-UNGATED). Two of them + # (catalog, planner) are deps of lance-graph, so their LIBS compiled + # inside a gated build while their tests ran nowhere; the others compiled + # nowhere at all. That is how `lance-graph-hydrate` reached `main` + # un-compiling. + # + # Every step below was RUN LOCALLY on the pinned toolchain before being + # added, and the count in each comment is what it returned. A gate is + # only added for a crate that is green; nothing here is armed on hope. + - name: Run catalog tests (previously ungated) # 12 + 15 green + run: cargo test --manifest-path crates/lance-graph-catalog/Cargo.toml + - name: Run planner tests (previously ungated) # 368 + 4 green, 6 ignored + run: cargo test --manifest-path crates/lance-graph-planner/Cargo.toml + - name: Run ontology tests (previously ungated) # 276 + 2 + 6 green + run: cargo test --manifest-path crates/lance-graph-ontology/Cargo.toml + - name: Run rbac tests (previously ungated) # 23 green + run: cargo test --manifest-path crates/lance-graph-rbac/Cargo.toml + - name: Run archetype tests (previously ungated) # 16 green + run: cargo test --manifest-path crates/lance-graph-archetype/Cargo.toml + - name: Run consumer-conformance tests (previously ungated) # 8 green, 2 ignored + run: cargo test --manifest-path crates/lance-graph-consumer-conformance/Cargo.toml + - name: Run sigma-tier-router tests (previously ungated) # 20 green + run: cargo test --manifest-path crates/sigma-tier-router/Cargo.toml + - name: Run neural-debug tests (previously ungated) # 11 green + run: cargo test --manifest-path crates/neural-debug/Cargo.toml + - name: Run shader-driver tests (previously ungated) # 107 + 2 green + run: cargo test --manifest-path crates/cognitive-shader-driver/Cargo.toml + # lance-graph-benches carries NO tests — it is a benches-only crate with + # one `harness = false` target (`graph_execution`). `cargo test` on it + # runs zero tests and would be a gate that cannot fail; the meaningful + # check is that the bench target still COMPILES, which is the same shape + # build.yml already uses for lance-graph's own benches. + - name: Check benches compile (previously ungated) # no tests by design + run: cargo check --manifest-path crates/lance-graph-benches/Cargo.toml --benches + # lance-graph-hydrate: the crate this whole sweep came out of. It is a + # workspace MEMBER and did not compile at #981 — `object_store 0.13.2`, a + # semver-compatible release already in the lockfile, moved `get`/`put` + # onto `ObjectStoreExt`. The compile fix rides with the crate's own PR + # (claude/hydrate-from-zip); the GATE belongs here with the other nine. + - name: Run hydrate tests (previously ungated) # 33 green + run: cargo test --manifest-path crates/lance-graph-hydrate/Cargo.toml # bgz-tensor: workspace-EXCLUDED metric-algebraic codec crate (deps the # ndarray sibling checked out above + in-repo holograph). None of the # steps above reach it, so its 207 lib tests — including the V3 diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml index 26e1486fa..923d7676b 100644 --- a/.github/workflows/style.yml +++ b/.github/workflows/style.yml @@ -159,6 +159,38 @@ jobs: # rust-test.yml step for why. - name: Rustfmt causal-edge (workspace-excluded, previously ungated) run: cargo fmt --manifest-path crates/causal-edge/Cargo.toml -- --check + # The same ten crates the test steps above arm. All ten verified + # rustfmt-clean locally on the pinned toolchain before this was added — + # `sigma-tier-router` was NOT, and is formatted in this commit (14 hunks + # in one file, rustfmt output only, its 20 tests unchanged). Nine were + # already clean and simply had no gate holding them there. + # + # Formatting only. Clippy is deliberately NOT gated for these: it has not + # been measured crate-by-crate, and `causal-edge` alone already carries 7 + # pre-existing findings — arming a lint gate on unmeasured crates would + # fail PRs for defects they did not introduce. + - name: Rustfmt lance-graph-catalog (previously ungated) + run: cargo fmt --manifest-path crates/lance-graph-catalog/Cargo.toml -- --check + - name: Rustfmt lance-graph-planner (previously ungated) + run: cargo fmt --manifest-path crates/lance-graph-planner/Cargo.toml -- --check + - name: Rustfmt lance-graph-ontology (previously ungated) + run: cargo fmt --manifest-path crates/lance-graph-ontology/Cargo.toml -- --check + - name: Rustfmt lance-graph-rbac (previously ungated) + run: cargo fmt --manifest-path crates/lance-graph-rbac/Cargo.toml -- --check + - name: Rustfmt lance-graph-archetype (previously ungated) + run: cargo fmt --manifest-path crates/lance-graph-archetype/Cargo.toml -- --check + - name: Rustfmt lance-graph-consumer-conformance (previously ungated) + run: cargo fmt --manifest-path crates/lance-graph-consumer-conformance/Cargo.toml -- --check + - name: Rustfmt sigma-tier-router (previously ungated) + run: cargo fmt --manifest-path crates/sigma-tier-router/Cargo.toml -- --check + - name: Rustfmt neural-debug (previously ungated) + run: cargo fmt --manifest-path crates/neural-debug/Cargo.toml -- --check + - name: Rustfmt cognitive-shader-driver (previously ungated) + run: cargo fmt --manifest-path crates/cognitive-shader-driver/Cargo.toml -- --check + - name: Rustfmt lance-graph-benches (previously ungated) + run: cargo fmt --manifest-path crates/lance-graph-benches/Cargo.toml -- --check + - name: Rustfmt lance-graph-hydrate (previously ungated) + run: cargo fmt --manifest-path crates/lance-graph-hydrate/Cargo.toml -- --check # deepnsm is a standalone, workspace-excluded codec crate, so # `cargo fmt --all` never reaches it. It was brought to a rustfmt-clean # baseline in this PR; check it explicitly so it can't silently drift. diff --git a/crates/lance-graph-hydrate/src/copy.rs b/crates/lance-graph-hydrate/src/copy.rs index feda29f68..c54a1d53a 100644 --- a/crates/lance-graph-hydrate/src/copy.rs +++ b/crates/lance-graph-hydrate/src/copy.rs @@ -36,7 +36,10 @@ use crate::publish::{publish_by_rename, remove_staging, PublishError, StagingKind}; use crate::staging::staging_suffix; use futures::TryStreamExt; -use object_store::{path::Path as ObjPath, ObjectStore}; +// `get` / `put` moved onto an EXTENSION trait in object_store 0.13.2; the base +// `ObjectStore` trait no longer carries them. Without this import the crate +// does not compile at all — which went unnoticed because no CI job reached it. +use object_store::{path::Path as ObjPath, ObjectStore, ObjectStoreExt}; use std::path::{Path as FsPath, PathBuf}; use thiserror::Error; @@ -160,7 +163,10 @@ mod tests { let remote_tmp = tempfile::tempdir().expect("remote tempdir"); let store = store_at(remote_tmp.path()); store - .put(&ObjPath::from("ds/data.lance"), b"row-bytes".to_vec().into()) + .put( + &ObjPath::from("ds/data.lance"), + b"row-bytes".to_vec().into(), + ) .await .expect("put data file"); store @@ -194,7 +200,11 @@ mod tests { .filter_map(|e| e.ok()) .map(|e| e.file_name()) .collect(); - assert_eq!(siblings.len(), 1, "only the published dir should remain: {siblings:?}"); + assert_eq!( + siblings.len(), + 1, + "only the published dir should remain: {siblings:?}" + ); } #[tokio::test] diff --git a/crates/lance-graph-hydrate/src/dirty.rs b/crates/lance-graph-hydrate/src/dirty.rs index b01aa5b20..cf3beb1ac 100644 --- a/crates/lance-graph-hydrate/src/dirty.rs +++ b/crates/lance-graph-hydrate/src/dirty.rs @@ -109,7 +109,8 @@ mod tests { let dir = tempfile::tempdir().expect("tempdir"); let path = dir.path().join("ds.lance"); let schema = schema(); - let reader = RecordBatchIterator::new(vec![Ok(batch(&schema, 5))].into_iter(), schema.clone()); + let reader = + RecordBatchIterator::new(vec![Ok(batch(&schema, 5))].into_iter(), schema.clone()); Dataset::write( reader, path.to_str().unwrap(), @@ -121,7 +122,9 @@ mod tests { .await .expect("write v1"); - let ds = Dataset::open(path.to_str().unwrap()).await.expect("open v1"); + let ds = Dataset::open(path.to_str().unwrap()) + .await + .expect("open v1"); let hydrated_at = ds.version_id(); // A local-only append, simulating drift since hydration. @@ -148,7 +151,8 @@ mod tests { let dir = tempfile::tempdir().expect("tempdir"); let path = dir.path().join("ds.lance"); let schema = schema(); - let reader = RecordBatchIterator::new(vec![Ok(batch(&schema, 5))].into_iter(), schema.clone()); + let reader = + RecordBatchIterator::new(vec![Ok(batch(&schema, 5))].into_iter(), schema.clone()); Dataset::write( reader, path.to_str().unwrap(), diff --git a/crates/lance-graph-hydrate/src/file.rs b/crates/lance-graph-hydrate/src/file.rs index 526aa3b36..2f643f514 100644 --- a/crates/lance-graph-hydrate/src/file.rs +++ b/crates/lance-graph-hydrate/src/file.rs @@ -17,7 +17,10 @@ use crate::publish::{publish_by_rename, remove_staging, PublishError, StagingKind}; use crate::staging::staging_suffix; use futures::TryStreamExt; -use object_store::{path::Path as ObjPath, ObjectStore}; +// `get` / `put` moved onto an EXTENSION trait in object_store 0.13.2; the base +// `ObjectStore` trait no longer carries them. Without this import the crate +// does not compile at all — which went unnoticed because no CI job reached it. +use object_store::{path::Path as ObjPath, ObjectStore, ObjectStoreExt}; use sha2::{Digest, Sha256}; use std::path::{Path as FsPath, PathBuf}; use thiserror::Error; @@ -166,7 +169,11 @@ mod tests { .filter_map(|e| e.ok()) .map(|e| e.file_name()) .collect(); - assert_eq!(leftovers.len(), 1, "only the published file should remain: {leftovers:?}"); + assert_eq!( + leftovers.len(), + 1, + "only the published file should remain: {leftovers:?}" + ); } #[tokio::test] @@ -191,7 +198,10 @@ mod tests { .await .expect_err("must reject the wrong checksum"); assert!(matches!(err, HydrateFileError::ChecksumMismatch { .. })); - assert!(!publish_path.exists(), "no file should be published on mismatch"); + assert!( + !publish_path.exists(), + "no file should be published on mismatch" + ); let leftovers: Vec<_> = std::fs::read_dir(local_tmp.path()) .expect("read local tempdir") .filter_map(|e| e.ok()) diff --git a/crates/lance-graph-hydrate/src/marker.rs b/crates/lance-graph-hydrate/src/marker.rs index 2ab137d4b..7dc1d9c06 100644 --- a/crates/lance-graph-hydrate/src/marker.rs +++ b/crates/lance-graph-hydrate/src/marker.rs @@ -94,7 +94,7 @@ impl WarmMarker { /// permissive `>= 3` read. pub fn read(marker_path: &Path) -> Option { let text = fs::read_to_string(marker_path).ok()?; - let mut parts = text.trim().split_whitespace(); + let mut parts = text.split_whitespace(); if parts.next()? != MARKER_FORMAT_TAG { return None; } diff --git a/crates/lance-graph-hydrate/src/publish.rs b/crates/lance-graph-hydrate/src/publish.rs index 9b66540f9..a4537a7fa 100644 --- a/crates/lance-graph-hydrate/src/publish.rs +++ b/crates/lance-graph-hydrate/src/publish.rs @@ -146,7 +146,10 @@ mod tests { .await .expect("publish"); - assert_eq!(std::fs::read(&publish_path).expect("published file"), b"payload"); + assert_eq!( + std::fs::read(&publish_path).expect("published file"), + b"payload" + ); assert!(!staging.exists(), "staging file must be gone after publish"); } diff --git a/crates/sigma-tier-router/src/lib.rs b/crates/sigma-tier-router/src/lib.rs index 3b275aca5..5784303dc 100644 --- a/crates/sigma-tier-router/src/lib.rs +++ b/crates/sigma-tier-router/src/lib.rs @@ -31,7 +31,7 @@ //! The prior hand-tuned values are preserved as `SigmaTierBands::hand_tuned()` for //! backwards comparison. -use lance_graph_contract::mul::{GateDecision, i4_eval::gate_decision_i4}; +use lance_graph_contract::mul::{i4_eval::gate_decision_i4, GateDecision}; use lance_graph_contract::qualia::QualiaI4_16D; // ───────────────────────────────────────────────────────────────────────────── @@ -437,10 +437,10 @@ mod tests { /// → Calibrated trust + Flow state → GateDecision::Flow. fn flow_qualia() -> QualiaI4_16D { QualiaI4_16D::ZERO - .with(9, 5) // coherence (DIM_COHERENCE) - .with(1, 3) // valence - .with(2, 0) // tension (low) - .with(3, 5) // warmth + .with(9, 5) // coherence (DIM_COHERENCE) + .with(1, 3) // valence + .with(2, 0) // tension (low) + .with(3, 5) // warmth .with(14, 4) // groundedness } @@ -449,8 +449,8 @@ mod tests { /// Low coherence + high tension → Uncertain trust → Block. fn block_qualia() -> QualiaI4_16D { QualiaI4_16D::ZERO - .with(9, -5) // coherence (very low) - .with(2, 5) // tension (high) + .with(9, -5) // coherence (very low) + .with(2, 5) // tension (high) } // ── Test 1: default band thresholds are strictly monotonic ──────────────── @@ -470,7 +470,10 @@ mod tests { assert!( t[i] > t[i - 1], "threshold[{}]={} must be > threshold[{}]={}", - i, t[i], i - 1, t[i - 1] + i, + t[i], + i - 1, + t[i - 1] ); } } @@ -484,15 +487,15 @@ mod tests { // F values just at/below each threshold should give the correct tier. // Default: Σk upper bound = k * 0.10 let cases: &[(f32, u8)] = &[ - (0.05, 1), // below Σ1 threshold (0.10) - (0.10, 1), // exactly at Σ1 threshold - (0.15, 2), // between Σ1 and Σ2 - (0.20, 2), // exactly at Σ2 threshold - (0.55, 6), // between Σ5 and Σ6 - (0.90, 9), // exactly at Σ9 threshold - (0.95, 10), // between Σ9 and Σ10 → tier 10 - (1.00, 10), // exactly at Σ10 threshold - (1.10, 10), // above all thresholds → clamp to 10 + (0.05, 1), // below Σ1 threshold (0.10) + (0.10, 1), // exactly at Σ1 threshold + (0.15, 2), // between Σ1 and Σ2 + (0.20, 2), // exactly at Σ2 threshold + (0.55, 6), // between Σ5 and Σ6 + (0.90, 9), // exactly at Σ9 threshold + (0.95, 10), // between Σ9 and Σ10 → tier 10 + (1.00, 10), // exactly at Σ10 threshold + (1.10, 10), // above all thresholds → clamp to 10 ]; for &(f, expected_tier) in cases { let actual = bands.tier_for(f); @@ -573,7 +576,13 @@ mod tests { assert!(router.state.last_delta < 0.0, "expected F-falling delta"); let outcome = router.dispatch(&flow_qualia(), 3); assert!( - matches!(outcome, DispatchOutcome::Commit { tier_reached: 10, .. }), + matches!( + outcome, + DispatchOutcome::Commit { + tier_reached: 10, + .. + } + ), "Σ10 + F-falling should Commit, got {:?}", outcome ); @@ -637,9 +646,15 @@ mod tests { fn test_tick_updates_delta() { let mut router = make_router(0.0); let d1 = router.tick(0.5); - assert!((d1 - 0.5).abs() < 1e-6, "first tick delta should be 0.5 - 0.0 = 0.5"); + assert!( + (d1 - 0.5).abs() < 1e-6, + "first tick delta should be 0.5 - 0.0 = 0.5" + ); let d2 = router.tick(0.3); - assert!((d2 - (-0.2)).abs() < 1e-6, "second tick delta should be 0.3 - 0.5 = -0.2"); + assert!( + (d2 - (-0.2)).abs() < 1e-6, + "second tick delta should be 0.3 - 0.5 = -0.2" + ); assert!((router.state.last_delta - (-0.2)).abs() < 1e-6); assert!((router.state.current_f - 0.3).abs() < 1e-6); assert!((router.state.last_f - 0.5).abs() < 1e-6); @@ -655,7 +670,10 @@ mod tests { assert!((s.last_delta).abs() < 1e-9); s.update(0.7); - assert!((s.last_f).abs() < 1e-9, "last_f should be 0.0 after first update"); + assert!( + (s.last_f).abs() < 1e-9, + "last_f should be 0.0 after first update" + ); assert!((s.current_f - 0.7).abs() < 1e-6); assert!((s.last_delta - 0.7).abs() < 1e-6); @@ -694,7 +712,13 @@ mod tests { assert!(router.state.last_delta < 0.0); let outcome = router.dispatch(&flow_qualia(), 4); assert!( - matches!(outcome, DispatchOutcome::Commit { tier_reached: 10, .. }), + matches!( + outcome, + DispatchOutcome::Commit { + tier_reached: 10, + .. + } + ), "floor=0.0 + Σ10 + F-falling must Commit, got {:?}", outcome ); @@ -738,7 +762,10 @@ mod tests { assert!( deltas[i + 1] > deltas[i], "Jirak default must be convex: delta[{}]={:.6} must be < delta[{}]={:.6}", - i, deltas[i], i + 1, deltas[i + 1] + i, + deltas[i], + i + 1, + deltas[i + 1] ); } } @@ -755,7 +782,8 @@ mod tests { assert!( (t[0] - expected_sigma1).abs() < 1e-6, "Σ1 should be ≈ {:.7} (k^1.5/10^1.5), got {:.7}", - expected_sigma1, t[0] + expected_sigma1, + t[0] ); // Σ10 = 1.0 exactly (anchored) assert_eq!(t[9], 1.0_f32, "Σ10 must be exactly 1.0"); @@ -766,7 +794,9 @@ mod tests { #[test] fn test_hand_tuned_preserves_old_values() { let bands = SigmaTierBands::hand_tuned(); - let expected = [0.10_f32, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90, 1.00]; + let expected = [ + 0.10_f32, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90, 1.00, + ]; assert_eq!( bands.sigma1_to_sigma10, expected, "hand_tuned() must return exactly the sprint-11 linear baseline" @@ -813,7 +843,8 @@ mod tests { var_p4 > var_p3, "jirak_p(4.0) should have higher delta variance ({:.8}) than jirak_p(3.0) ({:.8}); \ p=4 has a larger Jirak tail correction (more convex spacing)", - var_p4, var_p3 + var_p4, + var_p3 ); } @@ -862,7 +893,10 @@ mod tests { assert!( t[i] < t[i + 1], "Jirak band: t[{}]={:.6} must be < t[{}]={:.6}", - i, t[i], i + 1, t[i + 1] + i, + t[i], + i + 1, + t[i + 1] ); } } From b30f2c283abd592a6391c63ab817469a4bfc2a6f Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 22 Aug 2026 14:37:47 +0000 Subject: [PATCH 3/8] deps: one place per version, and a workspace-scope build gate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TWO CHANGES, one theme: the things that must not drift were declared in eight places, and the gate that would have caught a drifting member did not exist. [workspace.dependencies] — 33 declarations, 8 member manifests `arrow`, `datafusion`, the lance family and `object_store` were spelled a dozen ways across eight members (`arrow = "58"`, `{ version = "58" }`, `{ version = "58", optional = true }`, …). A bump was eight edits and an inconsistent one stayed invisible until a resolve produced two majors. Members now inherit with `workspace = true` and keep their own `optional` and `features`; `default-features = false` moved into the central entry for the two crates that need it (`datafusion`, `lancedb`), since every member already wanted it off. WHAT THIS DOES NOT DO, stated because it is the thing most likely to be misread: it does not pin the transitive closure. Cargo has no recursive version pin — `[workspace.dependencies]` centralises OUR declarations, `=x.y.z` pins only edges we declare, and `[patch]` redirects a source rather than a version graph. What `lancedb` pulls in as ITS datafusion is governed by lancedb's requirement. The artifact that freezes the whole graph is `Cargo.lock` — which is why this workspace commits 29 of them, and why FLOAT AND FIX (MedCare-rs, OGAR) does not transfer here. The falsifier that matters: `Cargo.lock` is BYTE-IDENTICAL after the change. Centralising moved the declarations, not the resolution. `cargo metadata` exits 0; the resolved pins are unchanged (lance 9.0.0, lancedb 0.33.0, datafusion 54.1.0, arrow 58.3.0, object_store 0.13.2) and datafusion is a single major — deltalake was the only thing that pulled 53 and #962 removed it. Two grep traps deliberately left alone: `lance-graph-planner`'s `datafusion = []` and `holograph`'s `lancedb = ["dep:lance"]` are FEATURE names, not dependencies. And `[workspace.dependencies]` reaches members only, so the excluded crates (`holograph`, `lance-graph-python`, …) keep their own declarations by necessity, not by oversight. cargo build --workspace as the structural gate Every other gate here names one crate by path, so `[workspace] members` gates nothing — which is how `lance-graph-hydrate` reached `main` un-compiling. This step needs no maintenance: a member added tomorrow is covered the moment it is listed. Measured on a clean tree with the manifest's `debug = 0`: exit 0, 6m18s, target/ 3.5 GB. It gates the COMPILE, not the tests, and the comment in the workflow says plainly why that is provisional rather than principled: an earlier `cargo test --workspace --no-run` hit ENOSPC, but that ran against a target already holding 15 GB of debug-laden artifacts from builds predating the manifest default — it measured the polluted tree, not the test scope. An earlier draft of this commit cited that number as if it were a property of the test scope; it is not, and the claim is removed rather than softened. When a clean-tree measurement exists, the ten per-crate test steps collapse into one. --- .github/workflows/rust-test.yml | 29 ++++++++++++++++++++ Cargo.toml | 35 ++++++++++++++++++++++++ crates/lance-graph-archetype/Cargo.toml | 2 +- crates/lance-graph-benches/Cargo.toml | 6 ++-- crates/lance-graph-callcenter/Cargo.toml | 10 +++---- crates/lance-graph-catalog/Cargo.toml | 4 +-- crates/lance-graph-hydrate/Cargo.toml | 6 ++-- crates/lance-graph-ontology/Cargo.toml | 8 +++--- crates/lance-graph/Cargo.toml | 26 +++++++++--------- crates/surreal_container/Cargo.toml | 4 +-- 10 files changed, 97 insertions(+), 33 deletions(-) diff --git a/.github/workflows/rust-test.yml b/.github/workflows/rust-test.yml index 8acef66d8..de42f5a00 100644 --- a/.github/workflows/rust-test.yml +++ b/.github/workflows/rust-test.yml @@ -184,6 +184,35 @@ jobs: # ISS-CAUSAL-EDGE-CARRIES-SEVEN-PRE-EXISTING-CLIPPY-FINDINGS instead. - name: Run causal-edge tests (workspace-excluded, previously ungated) run: cargo test --manifest-path crates/causal-edge/Cargo.toml + # ── The structural net: every member, present and future ───────────── + # + # The per-crate steps below are a hand-maintained allowlist, and that is + # exactly how `lance-graph-hydrate` reached `main` un-compiling: it was a + # workspace MEMBER, but membership gates nothing when no job says + # `--workspace`. This step is the net that needs no maintenance — a member + # added tomorrow is covered the moment it is listed in `[workspace]`. + # + # Measured before landing (2026-08-22, clean tree, `[profile.dev] + # debug = 0` from the manifest): + # + # cargo build --workspace exit 0, 6m18s, target/ 3.5 GB + # + # BUILD and not TEST, for now, and honestly: whether `cargo test + # --workspace` also fits a runner has NOT been established here. An + # earlier attempt hit ENOSPC, but that was against a target already + # holding 15 GB of debug-laden artifacts from builds that predate the + # manifest default — it measured the polluted tree, not the test scope. + # Until a clean-tree measurement exists, the workspace-scope gate is the + # compile and the per-crate steps below carry the tests, each with its + # own feature resolution rather than the workspace-unified one. + # + # So a new member's TESTS still need a line below. That is a known edge, + # not an oversight, and it disappears the moment someone measures a + # workspace test run fitting the runner — at which point these ten steps + # collapse into one. + - name: Build every workspace member (structural gate) + run: cargo build --workspace + # ── The rest of the allowlist gap, closed in one pass ──────────────── # # No workflow here runs `--workspace`, so `[workspace] members` gates diff --git a/Cargo.toml b/Cargo.toml index cba2c55a8..f7202b782 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -237,6 +237,41 @@ resolver = "2" # compat lands OR a 0.16-line branch is added to the fork. # The patch is a declared-intent override; effect on the transitive depends # on semver compat. See BLOCKED(D) comment above + TD-NDARRAY-PATCH-0_16. +# ── One place per version, for the crates whose versions must move together ── +# +# These were declared in eight member manifests, in a dozen spellings +# (`arrow = "58"`, `{ version = "58" }`, `{ version = "58", optional = true }`, +# …). A bump was eight edits, and an inconsistent one was invisible until a +# resolve produced two majors. +# +# What this does NOT do, and cannot: pin the TRANSITIVE closure. Cargo has no +# recursive version pin — `[workspace.dependencies]` centralises OUR +# declarations, `=x.y.z` pins only the edges we declare, and `[patch]` +# redirects a source rather than a version graph. What `lancedb` pulls in as +# ITS datafusion is governed by lancedb's own requirement. The artifact that +# freezes the whole graph is `Cargo.lock`, which is why this workspace commits +# 29 of them and why FLOAT AND FIX (MedCare-rs, OGAR) does not transfer here. +# +# The lance family moves in EXACT lockstep (`=`); arrow and datafusion are +# caret by design, so the lockfile is what holds them still between bumps. +# datafusion is a SINGLE major here since #962 removed the `delta` feature — +# deltalake was the only thing that pulled 53, and it is gone (0 hits in the +# lock). +[workspace.dependencies] +lance = "=9.0.0" +lance-linalg = "=9.0.0" +lance-index = "=9.0.0" +lancedb = { version = "=0.33.0", default-features = false } +arrow = "58" +arrow-array = "58" +arrow-schema = "58" +datafusion = { version = "54", default-features = false } +datafusion-common = "54" +datafusion-expr = "54" +datafusion-sql = "54" +datafusion-functions-aggregate = "54" +object_store = { version = "0.13", features = ["aws"] } + [patch.crates-io] # Local sibling checkout of the SAME fork (P0: "prefer the local/fork source, # always"; the direct deps already assume the sibling via path = "../../../ndarray"). diff --git a/crates/lance-graph-archetype/Cargo.toml b/crates/lance-graph-archetype/Cargo.toml index 06aebcbdd..7e4278226 100644 --- a/crates/lance-graph-archetype/Cargo.toml +++ b/crates/lance-graph-archetype/Cargo.toml @@ -12,7 +12,7 @@ lance-graph-contract = { path = "../lance-graph-contract" } # workspace has no shared [workspace.dependencies] table today; using # explicit versions consistent with the rest of the codebase (arrow 57, # thiserror 2). See PR description for the single-line deviation. -arrow = "58" +arrow = { workspace = true } thiserror = "2" [dev-dependencies] diff --git a/crates/lance-graph-benches/Cargo.toml b/crates/lance-graph-benches/Cargo.toml index f1a80a4b7..056d8b385 100644 --- a/crates/lance-graph-benches/Cargo.toml +++ b/crates/lance-graph-benches/Cargo.toml @@ -7,9 +7,9 @@ description = "Benchmarks for lance-graph (not published)" [dependencies] lance-graph = { path = "../lance-graph", version = "0.5.3" } -lance = "=9.0.0" -arrow-array = "58" -arrow-schema = "58" +lance = { workspace = true } +arrow-array = { workspace = true } +arrow-schema = { workspace = true } criterion = { version = "0.5", features = ["async", "async_tokio", "html_reports"] } futures = "0.3" tempfile = "3" diff --git a/crates/lance-graph-callcenter/Cargo.toml b/crates/lance-graph-callcenter/Cargo.toml index af9bd5bf0..44bffc992 100644 --- a/crates/lance-graph-callcenter/Cargo.toml +++ b/crates/lance-graph-callcenter/Cargo.toml @@ -26,15 +26,15 @@ thiserror = "1" # [persist] — Arrow RecordBatch + Lance dataset ops # arrow bumped to 58 to match lance 6.0's transitive arrow version. # TODO(lance-bump): align rest of workspace on arrow 58 once feasible. -arrow = { version = "58", optional = true } -lance = { version = "=9.0.0", optional = true } +arrow = { workspace = true, optional = true } +lance = { workspace = true, optional = true } # [query] / [query-lite] — DataFusion. Base dep has default-features = false. # The full `query` feature adds datafusion/default (compression backends etc). # The `query-lite` feature gets logical plan + optimizer without compression, # avoiding the xz2/liblzma `links = "lzma"` collision (lance v2 era; may be # resolved with lance 4 + datafusion 52 — verify and remove if so). -datafusion = { version = "54", optional = true, default-features = false, features = ["unicode_expressions"] } +datafusion = { workspace = true, optional = true, features = ["unicode_expressions"] } # [realtime] — version watcher, Phoenix channel shapes, WebSocket tokio = { version = "1", features = ["sync", "rt-multi-thread", "macros", "time"], optional = true } @@ -49,8 +49,8 @@ flate2 = { version = "1", optional = true } # [lance-sink] — LanceAuditSink: arrow-array + arrow-schema + lance + tokio runtime # TODO(lance-bump): arrow-array/arrow-schema bumped to 58 for lance 6.0 compat. -arrow-array = { version = "58", optional = true } -arrow-schema = { version = "58", optional = true } +arrow-array = { workspace = true, optional = true } +arrow-schema = { workspace = true, optional = true } # verify binary — clap (only needed when building the bin) clap = { version = "4", features = ["derive"], optional = true } diff --git a/crates/lance-graph-catalog/Cargo.toml b/crates/lance-graph-catalog/Cargo.toml index 500a0361c..58254aee4 100644 --- a/crates/lance-graph-catalog/Cargo.toml +++ b/crates/lance-graph-catalog/Cargo.toml @@ -11,9 +11,9 @@ keywords = ["lance", "graph", "catalog", "namespace"] categories = ["database", "data-structures", "science"] [dependencies] -arrow-schema = "58" +arrow-schema = { workspace = true } async-trait = "0.1" -datafusion = { version = "54", default-features = false } +datafusion = { workspace = true } lance-namespace = "=9.0.0" reqwest = { version = "0.12", features = ["json"], optional = true } serde = { version = "1", features = ["derive"] } diff --git a/crates/lance-graph-hydrate/Cargo.toml b/crates/lance-graph-hydrate/Cargo.toml index 0988864c5..ea8789bab 100644 --- a/crates/lance-graph-hydrate/Cargo.toml +++ b/crates/lance-graph-hydrate/Cargo.toml @@ -25,8 +25,8 @@ description = "Generic SoA -> S3 -> local volume -> Lance hydration lifecycle: h license = "Apache-2.0" [dependencies] -lance = "=9.0.0" -object_store = { version = "0.13", features = ["aws"] } +lance = { workspace = true } +object_store = { workspace = true } futures = "0.3" tokio = { version = "1.37", features = ["fs", "rt", "macros"] } sha2 = "0.10" @@ -38,4 +38,4 @@ libc = "0.2" [dev-dependencies] tokio = { version = "1.37", features = ["rt-multi-thread", "macros"] } tempfile = "3" -arrow = { version = "58" } +arrow = { workspace = true } diff --git a/crates/lance-graph-ontology/Cargo.toml b/crates/lance-graph-ontology/Cargo.toml index 3798406c6..87f04024d 100644 --- a/crates/lance-graph-ontology/Cargo.toml +++ b/crates/lance-graph-ontology/Cargo.toml @@ -43,12 +43,12 @@ sha2 = "0.10" # Lance-backed dictionary cache is feature-gated so the crate compiles without # protoc (lance-encoding's build-time dep). The default in-memory registry is # the canonical surface for tests and consumers that don't need persistence. -lance = { version = "=9.0.0", optional = true } +lance = { workspace = true, optional = true } # arrow bumped to 58 to match lance 6.0's transitive arrow version. # TODO(lance-bump): align rest of workspace on arrow 58 once feasible. -arrow = { version = "58", optional = true } -arrow-array = { version = "58", optional = true } -arrow-schema = { version = "58", optional = true } +arrow = { workspace = true, optional = true } +arrow-array = { workspace = true, optional = true } +arrow-schema = { workspace = true, optional = true } tokio = { version = "1", default-features = false, features = ["rt", "macros", "fs"], optional = true } futures = { version = "0.3", optional = true } diff --git a/crates/lance-graph/Cargo.toml b/crates/lance-graph/Cargo.toml index fdfe4e8c5..23deb99a9 100644 --- a/crates/lance-graph/Cargo.toml +++ b/crates/lance-graph/Cargo.toml @@ -13,12 +13,12 @@ categories = ["database", "data-structures", "science"] [dependencies] # arrow + datafusion bumped to 58 + 53 to align with lance 6.0's transitive versions. # TODO(lance-bump): downstream crates may need follow-up to align. -arrow = { version = "58", features = ["prettyprint"] } -arrow-array = "58" -arrow-schema = "58" +arrow = { workspace = true, features = ["prettyprint"] } +arrow-array = { workspace = true } +arrow-schema = { workspace = true } chrono = "0.4" async-trait = "0.1" -datafusion = { version = "54", default-features = false, features = [ +datafusion = { workspace = true, features = [ "nested_expressions", "regex_expressions", "unicode_expressions", @@ -28,17 +28,17 @@ datafusion = { version = "54", default-features = false, features = [ "string_expressions", "parquet", ] } -datafusion-common = "54" -datafusion-expr = "54" -datafusion-sql = "54" -datafusion-functions-aggregate = "54" +datafusion-common = { workspace = true } +datafusion-expr = { workspace = true } +datafusion-sql = { workspace = true } +datafusion-functions-aggregate = { workspace = true } futures = "0.3" lance-graph-catalog = { path = "../lance-graph-catalog", version = "0.5.4" } lance-graph-contract = { path = "../lance-graph-contract" } -lance = "=9.0.0" -lance-linalg = "=9.0.0" +lance = { workspace = true } +lance-linalg = { workspace = true } lance-namespace = "=9.0.0" -lancedb = { version = "=0.33.0", optional = true, default-features = false } +lancedb = { workspace = true, optional = true } nom = "7.1" serde = { version = "1", features = ["derive"] } serde_json = "1" @@ -117,7 +117,7 @@ deepnsm = { path = "../deepnsm" } lance-graph-contract = { path = "../lance-graph-contract" } futures = "0.3" lance-arrow = "=9.0.0" -lance-index = "=9.0.0" +lance-index = { workspace = true } tempfile = "3" tokio = { version = "1.37", features = ["macros", "rt-multi-thread"] } causal-edge = { path = "../causal-edge" } @@ -141,4 +141,4 @@ lance-graph-planner = { path = "../lance-graph-planner" } # today (cargo unifies it with what lance-io already turns on); it makes the # capability this crate USES a thing this crate ASKS FOR. Same declaration q2's # `cockpit-server` already carries for the same reason. -object_store = { version = "0.13", features = ["aws"] } +object_store = { workspace = true } diff --git a/crates/surreal_container/Cargo.toml b/crates/surreal_container/Cargo.toml index 891b30c52..fe0e7696f 100644 --- a/crates/surreal_container/Cargo.toml +++ b/crates/surreal_container/Cargo.toml @@ -50,8 +50,8 @@ Depends on task 01 (deps_substrate) being resolved before compiling. # Contract types the view glove (`view.rs`) implements. lance-graph-contract = { path = "../lance-graph-contract" } -lance = "=9.0.0" -lancedb = { version = "=0.33.0", optional = true, default-features = false } +lance = { workspace = true } +lancedb = { workspace = true, optional = true } # Runtime support (not blocked — these are well-known, confirmed versions) tokio = { version = "1", features = ["rt-multi-thread", "macros"] } From ff3e34b4b3881304eba127ca26df21d45d35df75 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 22 Aug 2026 14:40:30 +0000 Subject: [PATCH 4/8] ci: the workspace gate's build-not-test choice, now measured instead of assumed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous commit landed `cargo build --workspace` as the structural gate and said openly that the build-not-test choice was provisional: the only evidence against a test-scope job was an ENOSPC that had measured a polluted 15 GB target rather than the test scope itself. Measured now, clean tree, manifest `debug = 0`: cargo build --workspace exit 0, 6m18s, target/ 3.5 GB cargo test --workspace --no-run exit 0, target/ 14 GB, 86 binaries The test scope SUCCEEDS. What rules it out is not failure but size: 14 GB is the same order as a GitHub runner's free disk, against 3.5 GB for the compile. So the choice stands, for a reason that is now a measurement rather than an artifact of my own polluted tree. Also recorded, because it is a risk this branch INTRODUCED rather than found: the ten per-crate test steps run in this same job and accumulate their test binaries into the same target. They are a subset of those 14 GB, so this job's disk headroom is not proven — only the workspace compile is. If it ever fails on disk, the fix is to split those tests into their own job, not to drop the gate. --- .github/workflows/rust-test.yml | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/.github/workflows/rust-test.yml b/.github/workflows/rust-test.yml index de42f5a00..79f15de3d 100644 --- a/.github/workflows/rust-test.yml +++ b/.github/workflows/rust-test.yml @@ -195,21 +195,26 @@ jobs: # Measured before landing (2026-08-22, clean tree, `[profile.dev] # debug = 0` from the manifest): # - # cargo build --workspace exit 0, 6m18s, target/ 3.5 GB + # cargo build --workspace exit 0, 6m18s, target/ 3.5 GB + # cargo test --workspace --no-run exit 0, target/ 14 GB, 86 binaries # - # BUILD and not TEST, for now, and honestly: whether `cargo test - # --workspace` also fits a runner has NOT been established here. An - # earlier attempt hit ENOSPC, but that was against a target already - # holding 15 GB of debug-laden artifacts from builds that predate the - # manifest default — it measured the polluted tree, not the test scope. - # Until a clean-tree measurement exists, the workspace-scope gate is the - # compile and the per-crate steps below carry the tests, each with its - # own feature resolution rather than the workspace-unified one. + # BUILD and not TEST, and the reason is the second row. The test scope + # SUCCEEDS — an earlier ENOSPC was against a target already holding 15 GB + # of debug-laden artifacts from builds predating the manifest default, and + # measured that tree rather than the test scope. What rules it out here is + # size: linking every member's test binaries costs 14 GB, which is the + # same order as a GitHub runner's free disk. The compile costs 3.5 GB. # - # So a new member's TESTS still need a line below. That is a known edge, - # not an oversight, and it disappears the moment someone measures a - # workspace test run fitting the runner — at which point these ten steps - # collapse into one. + # KNOWN RISK, stated rather than discovered later: the per-crate test + # steps below run in THIS job and accumulate their test binaries into the + # same `target/`. They are a subset of those 14 GB, so this job's disk + # headroom is not proven — only the workspace compile is. If the job ever + # fails on disk, the fix is to split the per-crate tests into their own + # job (or `cargo clean -p` between them), not to drop the gate. + # + # A new member's TESTS therefore still need a line below. That edge + # disappears if the runner is ever measured to hold the 14 GB, at which + # point these ten steps collapse into one. - name: Build every workspace member (structural gate) run: cargo build --workspace From ee3e7069dd0e1b078113e9f3ff7537bb058f4696 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 22 Aug 2026 14:49:25 +0000 Subject: [PATCH 5/8] ci: split the additions into their own job, and gate the two members my check could not see MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TWO THINGS, and the second is a correction to this branch's own claim. Own job, preventively Everything this branch added to `test` — the workspace compile gate plus the per-crate steps for members that had none — accumulates test binaries into one `target/`. `test` is a job with a MEASURED history at exactly that cliff; its own env block records "a hard `ld` SIGBUS (signal 7 = object file truncated when the runner partition fills mid-link)". Measured here on a clean tree with the manifest's `debug = 0`: `cargo build --workspace` costs 3.5 GB; `cargo test --workspace --no-run` costs 14 GB across 86 binaries — the same order as a runner's free disk. Adding a subset of that to a job already known to have filled its partition is a bet with no upside, so `test` goes back to exactly its pre-branch disk profile and every addition moves to `member-tests` with its own runner and its own cache key (sharing `lance-graph-deps` would have two jobs writing different contents under one key). The claim "every member is gated" was FALSE when I made it The member check extracted with `"crates/[a-z0-9-]+"` — no underscore. So `crates/surreal_container` was never in the list it checked, and `tools/dto-class-check` is not under `crates/` at all. Two of eighteen members were invisible to the check that reported full coverage, and the "25 members" figure in earlier messages came from the same broken extraction (it counted comment lines in a `sed` range). A membership check that cannot see two of its inputs is the same shape as a gate that cannot fire — the exact defect class this branch exists to close, one level up, in my own instrument. Both now gated, both measured first: `surreal_container` 5 + 5 green, `dto-class-check` 1 green. `surreal_container` was also rustfmt-dirty (6 hunks in one test file, formatted here, tests unchanged). Re-checked with a parser that reads the `members` array instead of a line range: 18 members, none without a job. --- .github/workflows/rust-test.yml | 144 +++++++++++++----- .github/workflows/style.yml | 8 + .../surreal_container/tests/scheduler_seam.rs | 47 +++++- 3 files changed, 154 insertions(+), 45 deletions(-) diff --git a/.github/workflows/rust-test.yml b/.github/workflows/rust-test.yml index 79f15de3d..0570a879e 100644 --- a/.github/workflows/rust-test.yml +++ b/.github/workflows/rust-test.yml @@ -184,6 +184,102 @@ jobs: # ISS-CAUSAL-EDGE-CARRIES-SEVEN-PRE-EXISTING-CLIPPY-FINDINGS instead. - name: Run causal-edge tests (workspace-excluded, previously ungated) run: cargo test --manifest-path crates/causal-edge/Cargo.toml + # bgz-tensor: workspace-EXCLUDED metric-algebraic codec crate (deps the + # ndarray sibling checked out above + in-repo holograph). None of the + # steps above reach it, so its 207 lib tests — including the V3 + # `morton_cascade` Fisher-z compute (E-MORTON-CASCADE-V3-1) — only ever + # ran on a developer machine. A green CI that never built the new module + # is exactly the "blind gate" this repo has repeatedly closed + # (deepnsm / supervisor / ogar above). One scoped step arms it. Gating. + - name: Run bgz-tensor tests (workspace-excluded, ndarray sibling) + run: cargo test --manifest-path crates/bgz-tensor/Cargo.toml --lib + # lance-graph-callcenter UNDER `--features query` — the same blind gate as + # supervisor above, but one level subtler: the crate was not merely + # untested, it has `default = []`, so even a bare `cargo test` on it would + # compile almost nothing and report a green it had not earned. + # + # Measured, not hypothetical. Running this combination by hand on + # 2026-08-05 found 209 passed / 2 FAILED: the `graph_table` fixture minted + # rows with the V1 `CLASSID_OSINT` while `OSINT_GOTHAM.classid` resolves to + # `CLASSID_OSINT_V3` under the default `guid-v3-tail`, so + # `project_snapshot`'s class filter matched zero of two rows and every + # assertion ran against an empty snapshot (fixed in #897; recorded as + # E-A-FEATURE-CONDITIONAL-CLASSID-SILENTLY-EMPTIED-A-FIXTURE-1). The same + # feature gap had already produced a clippy EXIT=0 over never-compiled + # `vsa_udfs` / `transcode::ontology_table` earlier the same day. + # + # 211 tests, ~2 min incremental. Gating. + - name: Run callcenter tests --features query (blind-gate closure) + run: cargo test --manifest-path crates/lance-graph-callcenter/Cargo.toml --features query --lib + # Codex P2 (PR #861): the two self-asserting probe examples validate + # their fixtures via assert! inside example main() — cargo test never + # executes an example's main(), so those asserts only ever ran on a + # developer's local `cargo run --example` invocation. Running the + # examples explicitly here closes that blind-gate (same posture as + # deepnsm / supervisor / bgz-tensor above). + - name: Probe falsifier - eyes opened (fixture asserts) + run: cargo run -p lance-graph-planner --example probe_eyes_opened + - name: Probe falsifier - babel stances (fixture asserts) + run: cargo run -p lance-graph-planner --example probe_babel_stances + + + # ── Split out of `test` DELIBERATELY, before it broke ───────────────────── + # + # Everything below was added to the `test` job in this branch: the workspace + # compile gate plus ten per-crate test steps for members that had no gate at + # all. They accumulate their test binaries into one `target/`, and `test` is + # a job with a MEASURED history at exactly that cliff — its own env block + # records "a hard `ld` SIGBUS (signal 7 = object file truncated when the + # runner partition fills mid-link)". + # + # Measured here (clean tree, manifest `debug = 0`): `cargo build --workspace` + # costs 3.5 GB, `cargo test --workspace --no-run` costs 14 GB across 86 + # binaries — the same order as a runner's free disk. Adding a subset of that + # to a job already known to have filled its partition is a bet with no upside. + # + # So `test` goes back to exactly the disk profile it had before this branch, + # and every addition lives here with its own runner and its own cache key. + # This is preventive: the split is cheaper than the flake it avoids, and a + # flake here would be indistinguishable from a real failure. + member-tests: + runs-on: ubuntu-24.04 + timeout-minutes: 30 + env: + # Same reason as `test`: CI never opens a debugger, and debug info is + # what pushes the link over the partition. See that job's env block. + RUSTFLAGS: "-C debuginfo=0 -C target-cpu=x86-64-v3" + defaults: + run: + working-directory: lance-graph + steps: + - uses: actions/checkout@v4 + with: + path: lance-graph + - name: Checkout AdaWorldAPI/ndarray (sibling dependency) + uses: actions/checkout@v4 + with: + repository: AdaWorldAPI/ndarray + path: ndarray + - name: Checkout AdaWorldAPI/OGAR (sibling dependency, NO-PIN path deps) + uses: actions/checkout@v4 + with: + repository: AdaWorldAPI/OGAR + path: OGAR + - name: Setup rust toolchain (pinned by rust-toolchain.toml) + run: rustup show + - name: Setup mold linker + uses: rui314/setup-mold@9c9c13bf4c3f1adef0cc596abc155580bcb04444 # v1 + # Its OWN cache key. Sharing `lance-graph-deps` with `test` would have two + # jobs writing different contents under one key and thrashing it. + - uses: Swatinem/rust-cache@v2 + with: + shared-key: "lance-graph-members" + workspaces: | + lance-graph + - name: Install dependencies + run: | + sudo apt update + sudo apt install -y protobuf-compiler # ── The structural net: every member, present and future ───────────── # # The per-crate steps below are a hand-maintained allowlist, and that is @@ -264,43 +360,17 @@ jobs: # (claude/hydrate-from-zip); the GATE belongs here with the other nine. - name: Run hydrate tests (previously ungated) # 33 green run: cargo test --manifest-path crates/lance-graph-hydrate/Cargo.toml - # bgz-tensor: workspace-EXCLUDED metric-algebraic codec crate (deps the - # ndarray sibling checked out above + in-repo holograph). None of the - # steps above reach it, so its 207 lib tests — including the V3 - # `morton_cascade` Fisher-z compute (E-MORTON-CASCADE-V3-1) — only ever - # ran on a developer machine. A green CI that never built the new module - # is exactly the "blind gate" this repo has repeatedly closed - # (deepnsm / supervisor / ogar above). One scoped step arms it. Gating. - - name: Run bgz-tensor tests (workspace-excluded, ndarray sibling) - run: cargo test --manifest-path crates/bgz-tensor/Cargo.toml --lib - # lance-graph-callcenter UNDER `--features query` — the same blind gate as - # supervisor above, but one level subtler: the crate was not merely - # untested, it has `default = []`, so even a bare `cargo test` on it would - # compile almost nothing and report a green it had not earned. - # - # Measured, not hypothetical. Running this combination by hand on - # 2026-08-05 found 209 passed / 2 FAILED: the `graph_table` fixture minted - # rows with the V1 `CLASSID_OSINT` while `OSINT_GOTHAM.classid` resolves to - # `CLASSID_OSINT_V3` under the default `guid-v3-tail`, so - # `project_snapshot`'s class filter matched zero of two rows and every - # assertion ran against an empty snapshot (fixed in #897; recorded as - # E-A-FEATURE-CONDITIONAL-CLASSID-SILENTLY-EMPTIED-A-FIXTURE-1). The same - # feature gap had already produced a clippy EXIT=0 over never-compiled - # `vsa_udfs` / `transcode::ontology_table` earlier the same day. - # - # 211 tests, ~2 min incremental. Gating. - - name: Run callcenter tests --features query (blind-gate closure) - run: cargo test --manifest-path crates/lance-graph-callcenter/Cargo.toml --features query --lib - # Codex P2 (PR #861): the two self-asserting probe examples validate - # their fixtures via assert! inside example main() — cargo test never - # executes an example's main(), so those asserts only ever ran on a - # developer's local `cargo run --example` invocation. Running the - # examples explicitly here closes that blind-gate (same posture as - # deepnsm / supervisor / bgz-tensor above). - - name: Probe falsifier - eyes opened (fixture asserts) - run: cargo run -p lance-graph-planner --example probe_eyes_opened - - name: Probe falsifier - babel stances (fixture asserts) - run: cargo run -p lance-graph-planner --example probe_babel_stances + # These two were missed by the FIRST sweep on this branch, and the miss was + # in the measuring, not the workflow: the check extracted members with + # `"crates/[a-z0-9-]+"` — no underscore — so `surreal_container` was never + # in the list, and `tools/dto-class-check` is not under `crates/` at all. + # The claim "every member is gated" was therefore false when it was made. + # A membership check that cannot see two of its inputs is the same shape + # as a gate that cannot fire. + - name: Run surreal_container tests (missed by the first sweep) # 5 + 5 green + run: cargo test --manifest-path crates/surreal_container/Cargo.toml + - name: Run dto-class-check tests (missed by the first sweep) # 1 green + run: cargo test --manifest-path tools/dto-class-check/Cargo.toml test-with-coverage: runs-on: ubuntu-24.04 diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml index 923d7676b..e41dee547 100644 --- a/.github/workflows/style.yml +++ b/.github/workflows/style.yml @@ -191,6 +191,14 @@ jobs: run: cargo fmt --manifest-path crates/lance-graph-benches/Cargo.toml -- --check - name: Rustfmt lance-graph-hydrate (previously ungated) run: cargo fmt --manifest-path crates/lance-graph-hydrate/Cargo.toml -- --check + # See the rust-test.yml note: these two were missed by the first sweep + # because the member check's own regex could not see them. + # `surreal_container` was rustfmt-dirty (6 hunks in one test file, + # formatted in this commit, its 5 + 5 tests unchanged). + - name: Rustfmt surreal_container (missed by the first sweep) + run: cargo fmt --manifest-path crates/surreal_container/Cargo.toml -- --check + - name: Rustfmt dto-class-check (missed by the first sweep) + run: cargo fmt --manifest-path tools/dto-class-check/Cargo.toml -- --check # deepnsm is a standalone, workspace-excluded codec crate, so # `cargo fmt --all` never reaches it. It was brought to a rustfmt-clean # baseline in this PR; check it explicitly so it can't silently drift. diff --git a/crates/surreal_container/tests/scheduler_seam.rs b/crates/surreal_container/tests/scheduler_seam.rs index bb482aee3..4b36399ef 100644 --- a/crates/surreal_container/tests/scheduler_seam.rs +++ b/crates/surreal_container/tests/scheduler_seam.rs @@ -36,7 +36,11 @@ fn full_rubicon_arc_lowers_to_legal_successors() { ]; for (i, (from, want_to)) in arc.iter().enumerate() { let mv = scheduler - .on_version(&view_at(*from), DatasetVersion(i as u64 + 1), ExecTarget::Native) + .on_version( + &view_at(*from), + DatasetVersion(i as u64 + 1), + ExecTarget::Native, + ) .unwrap_or_else(|| panic!("{from:?} must schedule a forward move")); assert_eq!(mv.from, *from, "move.from must echo the observed phase"); assert_eq!(mv.to, *want_to, "{from:?} must lower to {want_to:?}"); @@ -56,7 +60,10 @@ fn full_rubicon_arc_lowers_to_legal_successors() { fn absorbing_columns_schedule_no_move() { let scheduler = NextPhaseScheduler; for phase in [KanbanColumn::Commit, KanbanColumn::Prune] { - assert!(phase.is_absorbing(), "{phase:?} must be absorbing (precondition)"); + assert!( + phase.is_absorbing(), + "{phase:?} must be absorbing (precondition)" + ); assert!( scheduler .on_version(&view_at(phase), DatasetVersion(99), ExecTarget::Native) @@ -75,7 +82,11 @@ fn libet_anchor_only_on_sigma_commit_crossing() { let scheduler = NextPhaseScheduler; let crossing = scheduler - .on_version(&view_at(KanbanColumn::Planning), DatasetVersion(1), ExecTarget::Native) + .on_version( + &view_at(KanbanColumn::Planning), + DatasetVersion(1), + ExecTarget::Native, + ) .expect("Planning advances"); assert_eq!(crossing.to, KanbanColumn::CognitiveWork); assert_eq!( @@ -84,7 +95,11 @@ fn libet_anchor_only_on_sigma_commit_crossing() { "the Σ-commit crossing must open the canonical Libet window" ); - for from in [KanbanColumn::CognitiveWork, KanbanColumn::Evaluation, KanbanColumn::Plan] { + for from in [ + KanbanColumn::CognitiveWork, + KanbanColumn::Evaluation, + KanbanColumn::Plan, + ] { let mv = scheduler .on_version(&view_at(from), DatasetVersion(2), ExecTarget::Native) .expect("non-absorbing column advances"); @@ -104,12 +119,23 @@ fn libet_anchor_only_on_sigma_commit_crossing() { fn lowering_is_deterministic() { let scheduler = NextPhaseScheduler; let a = scheduler - .on_version(&view_at(KanbanColumn::CognitiveWork), DatasetVersion(7), ExecTarget::Jit) + .on_version( + &view_at(KanbanColumn::CognitiveWork), + DatasetVersion(7), + ExecTarget::Jit, + ) .expect("advances"); let b = scheduler - .on_version(&view_at(KanbanColumn::CognitiveWork), DatasetVersion(7), ExecTarget::Jit) + .on_version( + &view_at(KanbanColumn::CognitiveWork), + DatasetVersion(7), + ExecTarget::Jit, + ) .expect("advances"); - assert_eq!(a, b, "same (view, version, exec) must lower to the same move"); + assert_eq!( + a, b, + "same (view, version, exec) must lower to the same move" + ); } /// KILL-CONDITION: the `exec` backend selector must ride through the lowering @@ -118,7 +144,12 @@ fn lowering_is_deterministic() { #[test] fn exec_target_rides_onto_the_move() { let scheduler = NextPhaseScheduler; - for exec in [ExecTarget::Native, ExecTarget::Jit, ExecTarget::SurrealQl, ExecTarget::Elixir] { + for exec in [ + ExecTarget::Native, + ExecTarget::Jit, + ExecTarget::SurrealQl, + ExecTarget::Elixir, + ] { let mv = scheduler .on_version(&view_at(KanbanColumn::Planning), DatasetVersion(3), exec) .expect("advances"); From 85d5f465b19215915d5d73828ba7fbbbe2572007 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 22 Aug 2026 14:53:37 +0000 Subject: [PATCH 6/8] ci: RUSTFLAGS no longer overrides the manifest's debug = 0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `build.yml`, `rust-test.yml`, `rust-publish.yml` and `style.yml` all set `RUSTFLAGS: "-C debuginfo=1 -C target-cpu=x86-64-v3"` at WORKFLOW level. RUSTFLAGS are appended after the flags cargo derives from the profile, so that `debuginfo=1` OVERRIDES `[profile.dev] debug = 0` in Cargo.toml — the manifest default was silently defeated in CI, which is the opposite of what it is for. Four jobs already carried a job-level `debuginfo=0` for exactly this reason, measured in b56bb2cd: ~930 MB -> ~252 MB per binary at link, -73%. Three did NOT, and inherited the `=1`: style.yml clippy compiles the whole tree style.yml format rust-publish.yml publish builds what gets published clippy in particular compiles everything and never opens a debugger. Now `debuginfo=0` at workflow level in all four. The four jobs with an explicit job-level `=0` keep the identical string, so their Swatinem cache keys are unchanged; the three that inherited `=1` get a new key and repopulate once. `RUST_BACKTRACE=1` still yields a backtrace, without line numbers. NOT changed, and both deliberately: - lance-graph's own Dockerfiles build `--release` and this workspace declares no `[profile.release]` at all, so cargo's default (`debug = false`) already applies. There is nothing to strip there. - MedCare-rs's Railway image is a different question and is left to its owner: its `[profile.release]` sets `debug = "line-tables-only"` + `strip = "none"`, and its Dockerfile states why — "line-tables-only debug info is intentionally kept for symbolicating crash dumps". That is a documented reason, which is exactly the carve-out in the standing rule ("debug 0 by default unless there are reasons"), so it is surfaced rather than stripped. --- .github/workflows/build.yml | 13 ++++++++++++- .github/workflows/rust-publish.yml | 13 ++++++++++++- .github/workflows/rust-test.yml | 13 ++++++++++++- .github/workflows/style.yml | 13 ++++++++++++- 4 files changed, 48 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 15fb96a47..13c3e0842 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,18 @@ concurrency: env: CARGO_TERM_COLOR: always - RUSTFLAGS: "-C debuginfo=1 -C target-cpu=x86-64-v3" + # debuginfo=0, not 1. RUSTFLAGS are appended AFTER the flags cargo derives + # from the profile, so a `debuginfo=1` here OVERRIDES `[profile.dev] debug = 0` + # in Cargo.toml — the manifest default is silently defeated in CI, which is the + # opposite of what it is for. CI never opens a debugger; `RUST_BACKTRACE=1` + # below still yields a backtrace, just without line numbers. + # + # Four jobs already carried a job-level `debuginfo=0` for exactly this reason + # (measured in b56bb2cd: ~930 MB -> ~252 MB per binary at link, -73%). They + # keep the identical string, so their Swatinem cache keys are unchanged; the + # jobs that until now INHERITED `=1` (style's clippy + format, rust-publish's + # publish) get a new key and repopulate once. + RUSTFLAGS: "-C debuginfo=0 -C target-cpu=x86-64-v3" RUST_BACKTRACE: "1" CARGO_INCREMENTAL: "0" diff --git a/.github/workflows/rust-publish.yml b/.github/workflows/rust-publish.yml index 157a61570..76ac48ea1 100644 --- a/.github/workflows/rust-publish.yml +++ b/.github/workflows/rust-publish.yml @@ -20,7 +20,18 @@ on: env: CARGO_TERM_COLOR: always - RUSTFLAGS: "-C debuginfo=1 -C target-cpu=x86-64-v3" + # debuginfo=0, not 1. RUSTFLAGS are appended AFTER the flags cargo derives + # from the profile, so a `debuginfo=1` here OVERRIDES `[profile.dev] debug = 0` + # in Cargo.toml — the manifest default is silently defeated in CI, which is the + # opposite of what it is for. CI never opens a debugger; `RUST_BACKTRACE=1` + # below still yields a backtrace, just without line numbers. + # + # Four jobs already carried a job-level `debuginfo=0` for exactly this reason + # (measured in b56bb2cd: ~930 MB -> ~252 MB per binary at link, -73%). They + # keep the identical string, so their Swatinem cache keys are unchanged; the + # jobs that until now INHERITED `=1` (style's clippy + format, rust-publish's + # publish) get a new key and repopulate once. + RUSTFLAGS: "-C debuginfo=0 -C target-cpu=x86-64-v3" RUST_BACKTRACE: "1" CARGO_INCREMENTAL: "0" CARGO_BUILD_JOBS: "1" diff --git a/.github/workflows/rust-test.yml b/.github/workflows/rust-test.yml index 0570a879e..b1d29e7d1 100644 --- a/.github/workflows/rust-test.yml +++ b/.github/workflows/rust-test.yml @@ -16,7 +16,18 @@ concurrency: env: CARGO_TERM_COLOR: always - RUSTFLAGS: "-C debuginfo=1 -C target-cpu=x86-64-v3" + # debuginfo=0, not 1. RUSTFLAGS are appended AFTER the flags cargo derives + # from the profile, so a `debuginfo=1` here OVERRIDES `[profile.dev] debug = 0` + # in Cargo.toml — the manifest default is silently defeated in CI, which is the + # opposite of what it is for. CI never opens a debugger; `RUST_BACKTRACE=1` + # below still yields a backtrace, just without line numbers. + # + # Four jobs already carried a job-level `debuginfo=0` for exactly this reason + # (measured in b56bb2cd: ~930 MB -> ~252 MB per binary at link, -73%). They + # keep the identical string, so their Swatinem cache keys are unchanged; the + # jobs that until now INHERITED `=1` (style's clippy + format, rust-publish's + # publish) get a new key and repopulate once. + RUSTFLAGS: "-C debuginfo=0 -C target-cpu=x86-64-v3" RUST_BACKTRACE: "1" CARGO_INCREMENTAL: "0" diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml index e41dee547..f3b209fd5 100644 --- a/.github/workflows/style.yml +++ b/.github/workflows/style.yml @@ -16,7 +16,18 @@ concurrency: env: CARGO_TERM_COLOR: always - RUSTFLAGS: "-C debuginfo=1 -C target-cpu=x86-64-v3" + # debuginfo=0, not 1. RUSTFLAGS are appended AFTER the flags cargo derives + # from the profile, so a `debuginfo=1` here OVERRIDES `[profile.dev] debug = 0` + # in Cargo.toml — the manifest default is silently defeated in CI, which is the + # opposite of what it is for. CI never opens a debugger; `RUST_BACKTRACE=1` + # below still yields a backtrace, just without line numbers. + # + # Four jobs already carried a job-level `debuginfo=0` for exactly this reason + # (measured in b56bb2cd: ~930 MB -> ~252 MB per binary at link, -73%). They + # keep the identical string, so their Swatinem cache keys are unchanged; the + # jobs that until now INHERITED `=1` (style's clippy + format, rust-publish's + # publish) get a new key and repopulate once. + RUSTFLAGS: "-C debuginfo=0 -C target-cpu=x86-64-v3" # Least-privilege: these jobs only read the repo (checkout + build + lint). permissions: From b9bbc631eba352f40b50f0de046d83a870a10b85 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 22 Aug 2026 15:00:33 +0000 Subject: [PATCH 7/8] Revert "ci: RUSTFLAGS no longer overrides the manifest's debug = 0" This reverts commit 85d5f465b19215915d5d73828ba7fbbbe2572007. --- .github/workflows/build.yml | 13 +------------ .github/workflows/rust-publish.yml | 13 +------------ .github/workflows/rust-test.yml | 13 +------------ .github/workflows/style.yml | 13 +------------ 4 files changed, 4 insertions(+), 48 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 13c3e0842..15fb96a47 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,18 +16,7 @@ concurrency: env: CARGO_TERM_COLOR: always - # debuginfo=0, not 1. RUSTFLAGS are appended AFTER the flags cargo derives - # from the profile, so a `debuginfo=1` here OVERRIDES `[profile.dev] debug = 0` - # in Cargo.toml — the manifest default is silently defeated in CI, which is the - # opposite of what it is for. CI never opens a debugger; `RUST_BACKTRACE=1` - # below still yields a backtrace, just without line numbers. - # - # Four jobs already carried a job-level `debuginfo=0` for exactly this reason - # (measured in b56bb2cd: ~930 MB -> ~252 MB per binary at link, -73%). They - # keep the identical string, so their Swatinem cache keys are unchanged; the - # jobs that until now INHERITED `=1` (style's clippy + format, rust-publish's - # publish) get a new key and repopulate once. - RUSTFLAGS: "-C debuginfo=0 -C target-cpu=x86-64-v3" + RUSTFLAGS: "-C debuginfo=1 -C target-cpu=x86-64-v3" RUST_BACKTRACE: "1" CARGO_INCREMENTAL: "0" diff --git a/.github/workflows/rust-publish.yml b/.github/workflows/rust-publish.yml index 76ac48ea1..157a61570 100644 --- a/.github/workflows/rust-publish.yml +++ b/.github/workflows/rust-publish.yml @@ -20,18 +20,7 @@ on: env: CARGO_TERM_COLOR: always - # debuginfo=0, not 1. RUSTFLAGS are appended AFTER the flags cargo derives - # from the profile, so a `debuginfo=1` here OVERRIDES `[profile.dev] debug = 0` - # in Cargo.toml — the manifest default is silently defeated in CI, which is the - # opposite of what it is for. CI never opens a debugger; `RUST_BACKTRACE=1` - # below still yields a backtrace, just without line numbers. - # - # Four jobs already carried a job-level `debuginfo=0` for exactly this reason - # (measured in b56bb2cd: ~930 MB -> ~252 MB per binary at link, -73%). They - # keep the identical string, so their Swatinem cache keys are unchanged; the - # jobs that until now INHERITED `=1` (style's clippy + format, rust-publish's - # publish) get a new key and repopulate once. - RUSTFLAGS: "-C debuginfo=0 -C target-cpu=x86-64-v3" + RUSTFLAGS: "-C debuginfo=1 -C target-cpu=x86-64-v3" RUST_BACKTRACE: "1" CARGO_INCREMENTAL: "0" CARGO_BUILD_JOBS: "1" diff --git a/.github/workflows/rust-test.yml b/.github/workflows/rust-test.yml index b1d29e7d1..0570a879e 100644 --- a/.github/workflows/rust-test.yml +++ b/.github/workflows/rust-test.yml @@ -16,18 +16,7 @@ concurrency: env: CARGO_TERM_COLOR: always - # debuginfo=0, not 1. RUSTFLAGS are appended AFTER the flags cargo derives - # from the profile, so a `debuginfo=1` here OVERRIDES `[profile.dev] debug = 0` - # in Cargo.toml — the manifest default is silently defeated in CI, which is the - # opposite of what it is for. CI never opens a debugger; `RUST_BACKTRACE=1` - # below still yields a backtrace, just without line numbers. - # - # Four jobs already carried a job-level `debuginfo=0` for exactly this reason - # (measured in b56bb2cd: ~930 MB -> ~252 MB per binary at link, -73%). They - # keep the identical string, so their Swatinem cache keys are unchanged; the - # jobs that until now INHERITED `=1` (style's clippy + format, rust-publish's - # publish) get a new key and repopulate once. - RUSTFLAGS: "-C debuginfo=0 -C target-cpu=x86-64-v3" + RUSTFLAGS: "-C debuginfo=1 -C target-cpu=x86-64-v3" RUST_BACKTRACE: "1" CARGO_INCREMENTAL: "0" diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml index f3b209fd5..e41dee547 100644 --- a/.github/workflows/style.yml +++ b/.github/workflows/style.yml @@ -16,18 +16,7 @@ concurrency: env: CARGO_TERM_COLOR: always - # debuginfo=0, not 1. RUSTFLAGS are appended AFTER the flags cargo derives - # from the profile, so a `debuginfo=1` here OVERRIDES `[profile.dev] debug = 0` - # in Cargo.toml — the manifest default is silently defeated in CI, which is the - # opposite of what it is for. CI never opens a debugger; `RUST_BACKTRACE=1` - # below still yields a backtrace, just without line numbers. - # - # Four jobs already carried a job-level `debuginfo=0` for exactly this reason - # (measured in b56bb2cd: ~930 MB -> ~252 MB per binary at link, -73%). They - # keep the identical string, so their Swatinem cache keys are unchanged; the - # jobs that until now INHERITED `=1` (style's clippy + format, rust-publish's - # publish) get a new key and repopulate once. - RUSTFLAGS: "-C debuginfo=0 -C target-cpu=x86-64-v3" + RUSTFLAGS: "-C debuginfo=1 -C target-cpu=x86-64-v3" # Least-privilege: these jobs only read the repo (checkout + build + lint). permissions: From f77683802214f840d11bae7da23d805937cced2f Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 22 Aug 2026 15:05:57 +0000 Subject: [PATCH 8/8] ci: fix the clippy step my toolchain edit folded into one command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Live CI failure on this branch: Run rustup show rustup component add clippy error: unrecognized subcommand 'rustup' The toolchain commit replaced a two-line `rustup toolchain install stable` / `rustup default stable` block with the single-line form `run: rustup show`. In `style.yml`'s clippy job that block had a THIRD line — `rustup component add clippy` — which the replacement did not match. Left behind under a single-line scalar, YAML folds the two into one command. Now a block scalar with both lines. The extra `component add` is redundant with `rust-toolchain.toml`'s `components = ["rustfmt", "clippy"]`, which `rustup show` already installs; it stays because it is idempotent and because removing it would be a second change in a step that just broke. WHY MY CHECK MISSED IT, since that is the more useful half: I validated with `yaml.safe_load`, which proves the file PARSES. `run: rustup show` followed by an indented line is perfectly valid YAML — a folded plain scalar. It parses to the wrong command. Parseability is not correctness, and a check that can only see the former will pass a broken workflow every time. The check is now on the parsed VALUE: it walks every step's `run`, splits it into lines, and fails on any line carrying two `rustup` invocations. Run across all seven workflows: the six other `rustup show` steps are single-line and clean, and clippy's is the intended two. --- .github/workflows/style.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml index e41dee547..760b29b6c 100644 --- a/.github/workflows/style.yml +++ b/.github/workflows/style.yml @@ -62,7 +62,12 @@ jobs: # set it as default; inside the repo the toolchain file won anyway, so # these jobs already ran on the pin while the workflow said otherwise. - name: Setup rust toolchain (pinned by rust-toolchain.toml) - run: rustup show + run: | + rustup show + # Redundant with `rust-toolchain.toml`'s `components = ["rustfmt", + # "clippy"]`, which `rustup show` already installs — kept because it + # is idempotent and because removing it would be a second change in a + # step that just broke. rustup component add clippy - uses: Swatinem/rust-cache@v2 with: