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..0570a879e 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 @@ -221,6 +222,156 @@ jobs: - 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 + # 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 + # cargo test --workspace --no-run exit 0, target/ 14 GB, 86 binaries + # + # 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. + # + # 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 + + # ── 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 + # 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 timeout-minutes: 30 @@ -258,10 +409,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..760b29b6c 100644 --- a/.github/workflows/style.yml +++ b/.github/workflows/style.yml @@ -54,10 +54,20 @@ jobs: with: repository: AdaWorldAPI/OGAR path: OGAR - - name: Setup rust 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 toolchain install stable - rustup default stable + 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: @@ -154,6 +164,46 @@ 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 + # 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/.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 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-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/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/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] ); } } 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"] } 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");