Added
- Rust crates now publish automatically on release.
.github/workflows/crates-publish.ymlpublishes the six public crates to crates.io in dependency order, authenticating tokenlessly throughrust-lang/crates-io-auth-action(GitHub OIDC), so noCARGO_REGISTRY_TOKENsecret exists.semantic_version.ymlinvokes it as aworkflow_calljob rather than relying on a tag trigger, because a tag pushed withGITHUB_TOKENdoes not start tag-triggered workflows -- the documented reason the PyPI and npm workflows still need a manual dispatch. The job skips any version already in the index, so a retry after a partial failure is safe, and it stampsworkspace.package.versionplus every internal[workspace.dependencies]entry from the release version so all crates ship in lockstep.
Changed
- BREAKING -- optional-backend and
narwhalsfloors now reflect versions that actually work. The previous lower bounds were never resolved or exercised by CI, and three of them named combinations that cannot function.narwhalsmoves from>=2.0to>=2.10.0-- it is the only mandatory runtime dependency, and only 2.10.0 onward honours thenarwhals.pluginsentry point xbbg registers, so on 2.0-2.9 the plugin never loads and any conversion raisesTypeError: Expected pandas-like dataframe, Polars dataframe, or Polars lazyframe, got: <class 'xbbg._core.ArrowTable'>. Thepandasextra moves from>=2.0to>=2.2.2,<4, because pandas 2.0.x declares nonumpy<2bound, so a fresh resolve pairs it with numpy 2.x and pandas then fails to import withnumpy.dtype size changed, may indicate binary incompatibility; 2.2.2 is the first release supporting numpy 2. Theduckdbextra moves from>=1.0to>=1.5.0, because below 1.5.0 the connection backing a returned relation is lost (Connection has already been closed), and 1.0.0 additionally cannot register an Arrow PyCapsule object at all.pytest-covin thetestextra gains a>=5.0bound; unpinned, a lowest-direct resolve selected pytest-cov 2.0.0 from 2018.polars>=0.20andpyarrow>=22.0.0were verified as genuinely working and are unchanged. Environments already on current releases are unaffected; anyone pinned below these bounds must upgrade. - Declared floors are now verified by CI. A new
floorsjob resolves the project withuv pip install --resolution lowest-directon Python 3.10 -- the lowest supported interpreter, and the only one where the oldest wheels are still selectable -- and runs the full non-live suite against the result. Each floor inpyproject.tomlcarries a comment recording the measured reason it cannot go lower. - Rust MSRV is declared.
[workspace.package]now setsrust-version = "1.88", matching the highestrust-versionin the resolved dependency graph, andpixi.tomlraises itsrustfloor from>=1.75to>=1.88to agree. The published sdist compiles the Rust extension on the user's machine, so the toolchain requirement is part of the package contract rather than a local development detail;>=1.75had been unsatisfiable for several majors. - Third-party Rust versions are centralized in
[workspace.dependencies]. The arrow family alone was restated across eight manifests andtokioacross five, and the disabledxbbg-cli/dotnet-xbbgmanifests had already drifted toarrow 57.1.0and acsbindgen 2that has never been published. Members now inherit with{ workspace = true }and layer only their ownfeatures.default-featuresis set in the workspace table because Cargo silently ignores a member'sdefault-features = falsewhen the workspace entry omits it. The migration is feature-neutral: resolved feature sets were diffed per package before and after. rmcpupgraded from 2.1.0 to 3.1.2 inxbbg-mcp, with no source changes required. Both versions defaultProtocolVersion::LATESTto MCP2025-11-25, so the advertised revision is unchanged; 3.x additionally negotiates the2026-07-28draft.- Internal crate versions are centralized too, and now carry a
version. Every intra-workspace dependency was a bare{ path = ... }.cargo publishrejects a path dependency with no version, so no crate with an internal dependency could be published at all -- the actual reason the Rust crates sat at 1.1.2 while the project tagged v1.4.6. The internal crates now sit in[workspace.dependencies]as{ path, version }and members inherit with{ workspace = true }, matching the convention already used for third-party crates. Because those entries setdefault-features = false,xbbg-mcpandxbbg-benchnow namefeatures = ["live"]explicitly where they previously inherited it via default features. - crates.io package metadata is complete and points at the project, not a personal account. All five published crates had
repositoryandhomepagepointing at a legacy personal GitHub account and itsgithub.iopages site; both now resolve to thexbbg-orgrepository andhttps://xbbg.org/.mainhad also dropped thehomepage,readme, andkeywordsthat 1.1.2 actually shipped. Every published crate now declareshomepage,readme,keywords,categories, andrust-version(crates.io reported the MSRV as unset for all of them), plus an explicitincludeallowlist so only source,Cargo.toml, andREADME.mdare ever packaged. blpapi-sysis renamed toxbbg-blpapi-systo match the name it is published under. The bareblpapi-sysname is taken on crates.io, so the local package name never matched the registry name andcargo publish -p xbbg-blpapi-syscould not resolve.[lib] name = "blpapi_sys"is retained, so Rust code still saysblpapi_sys::and the change is source-compatible with the published 1.1.2.exchanges.tomlmoved fromdefs/intocrates/xbbg-ext/data/.xbbg-extembeds it withinclude_str!, and the path reached outside the crate root, so the published sdist would not have contained the file and the crate would have been unbuildable from crates.io.xbbg-extwas its only consumer.
Removed
xbbg-sysis deleted. It was 16 lines that re-exportedblpapi_sys::*behind a mandatorylivefeature, with acompile_error!on every other configuration -- a leftover seam from the removed mock backend.xbbg_corenow depends onxbbg-blpapi-sysdirectly, and thecompile_error!guard moves toxbbg_core/src/lib.rsso a non-livebuild still fails with one clear message instead of dozens of unresolved imports. The crate should also be deleted on crates.io, which is possible only after a release wherexbbg_coreno longer depends on it.apps/xbbg-cliandbindings/dotnet-xbbgare deleted. Both were stubs of 16 and 10 lines, commented out of the workspace members, and had already drifted to dependency versions that do not resolve.
Fixed
- Engine teardown no longer panics when the engine is dropped inside an async context.
Engineowns its tokio runtime, and tokio refuses to release a runtime's last handle from inside another runtime because teardown must block to join worker threads. Any async owner therefore aborted withCannot drop a runtime in a context where blocking is not allowed-- includingxbbg-mcp, which holds the engine across#[tokio::main]and so panicked on every clean shutdown once an engine had started.Engine::dropnow takes the runtime out of its field and hands it toRuntime::shutdown_background()when a runtime is current, which is race-free because the field is left empty and no second release path can reach a zero refcount. Outside an async context the previous blocking drop is retained. The live integration test that usedstd::mem::forgetto dodge this panic -- and leaked the runtime doing so -- now drops the engine normally. - Engine startup failures report the real error instead of a runtime-teardown panic.
Engine::startbuilt its tokio runtime before constructing the worker pools, so any early return dropped the runtime on an async caller's thread and replaced the underlying failure with tokio's unrelated panic message. With a Bloomberg terminal unavailable,Engine::startnow surfacesfailed to spawn worker 0: session start failedrather thanCannot drop a runtime in a context where blocking is not allowed. The runtime is constructed after every fallible step. - Three high-severity advisories cleared from the JavaScript dev dependency trees (
postcssGHSA-fxqj-rqcc-2cmp / GHSA-r28c-9q8g-f849,nanoidGHSA-28wg-ghj8-5hjv / GHSA-2v37-7h3g-55p8,brace-expansionGHSA-mh99-v99m-4gvg / GHSA-rgw5-rvv9-x895), pluscryptographyGHSA-g6cj-pr64-35w5 andc-aresCVE-2026-33630 in the pixi environment. These are build- and test-time dependencies only and are not redistributed in any published artifact. - Stale
pyo3advisory ignores removed from.cargo/audit.tomlanddeny.toml. They suppressedRUSTSEC-2026-0176andRUSTSEC-2026-0177on the premise that the fix required a pyo3 0.29 chain that dependencies did not yet support; the workspace has since resolved to pyo3 0.29.2 exclusively, so the entries were unreachable and would have masked a future regression onto a vulnerable pyo3. blpapi-sysno longer advertises a documentation build it cannot perform. Itsdocs.rsmetadata requestedall-features = truewhilebuild.rspanics when the mutually exclusivestaticanddynamiclinkage features are both enabled, which also brokecargo clippy --all-featuresfor the whole workspace. The metadata now names the defaultdynamiclinkage.xbbg_corekeeps its underscore, permanently. crates.io normalizes-and_to the same package identity, soxbbg-coreis not an available name whilexbbg_coreexists, and claiming it would require deleting the crate and waiting out the name-reuse block. The manifest documents this so the inconsistency is not "corrected" later.
Full Changelog: v1.4.6...v1.4.7