Repository navigation
Conversation
…and enhance error handling in StationError
The pendant of weather-station-py, run for the language where the boundary is a C ABI: no interpreter mediates and the compiler stops helping at the extern "C". Built to find out what an FFI layer needs from the station crates before they reach a registry. Two artifacts rather than one, because Rust cannot export C++ — no class, no std::string, no std::function has an ABI stable even between two builds of the same compiler. src/lib.rs exports fourteen ws_* symbols; the C++ RAII and exception layer is header-only and compiled by the consumer's own toolchain. That is the same conclusion weather-station-py reached from the other end. The Python door's crate fixes carried, exactly as its README predicted the C ABI layer would inherit rather than re-solve them: shutdown(&self) is what lets ws_station_close take a const pointer, and is_closed() reading an atomic is what survives a log sink reentering the getters from aimdb's runtime thread mid-shutdown. What C adds: nothing may unwind across the boundary (catch_unwind at every entry point, and a noexcept trampoline in the header for the other direction), every argument is hostile (NULL and non-UTF-8 paths are refused, not mangled), and ownership has to be prose because free is the one entry point that is not thread-safe. What it found: after fork(), the child is told the station is open, its publish returns success, and the reading is dropped. Measured against a live broker. No fix here — pthread_atfork is process-global, and registering it on the application's behalf is the trespass this crate's predecessor already established a rule against. Findings in README.md; the twelve requirements they imply for aimdb core in REQUIREMENTS.md, four of them release-gating. Run it with `make spike-cpp` (needs mosquitto and a C++17 compiler). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MRVG2B9A8hfX8JMiXsW6nD
Fixes both, in patches/ against the sibling aimdb checkout, and records what tracing them turned up. Neither needed a new idea. The unbounded spin in AimDbHandle::new becomes a channel and one blocking_recv, which is the shape new_from_builder has used since it was written — a runtime that fails to start is now AttachFailed instead of a caller that never returns. The 10 ms poll in detach_internal becomes recv_timeout on a channel the helper thread sends through. The poll was the floor under every shutdown in every language binding: close() drops from 10 ms to 0-1 ms across four spike rounds. aimdb-sync no longer busy-waits anywhere. Two corrections and one escalation: CR-2's claim that the spin could hang ws_station_open_profile was wrong. There are two attach() entry points and weather-station takes the other one; the spin was never on this door's path. The second constructor is the original design-007 entry point, superseded once AimDbBuilder::build() went async and had to run inside the runtime thread. It has no in-tree callers left, which is why nothing exercised the spin. Worth keeping now that it is fixed, but it needs a test. CR-6 is not a long-standing API shape to reconsider. aimdb-sync's own changelog files it under Changed (breaking) for the unreleased 0.6.0: SyncConsumer's methods took &self and it was Clone + Sync in 0.5.0. The compiler confirms the loss of Sync and root-causes it to Box<dyn BufferReader<T> + Send> in aimdb-core's Reader<T>. That makes it the one finding so far that is release-gating rather than patchable later. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MRVG2B9A8hfX8JMiXsW6nD
Extends the busy-wait patch. The channel that replaced the spin now carries
Result<T, String> rather than a bare handle, so the three outcomes have
three arms: the value, the thread's own reason for failing, and a dropped
sender — the last being the one that used to be a hang.
The Err arm is an addition rather than a consequence of removing the spin,
and it is what an FFI consumer needs. A startup failure used to arrive as
"runtime thread failed to send handle" while the real io::Error went only
to a log sink the caller may never have installed. new_from_builder gets
the same treatment on both of its channels, which also recovers the reason
a database build failed.
Measured by forcing a build failure and catching at the C++ boundary:
before: Failed to attach database: Runtime thread failed to build database
after: Failed to attach database: Failed to build database: missing
parameter 'broker.url'
aimdb-sync is fmt clean, weather-station --features sync passes 16/2/4, and
make spike-cpp is all-green with close() at 0-1 ms.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRVG2B9A8hfX8JMiXsW6nD
Two passes of the review said different things about SyncConsumer and both were wrong the same way. REQUIREMENTS.md filed it as an API shape to reconsider; the last commit escalated it to the one release-gating finding. probes/cr6-consumer-shape says neither. &mut self is the honest signature: a reader is a cursor, poll_recv advances it, so reading mutates. The question was never whether it could be &self but what a consumer represents — and the answer makes the FFI shape fall out. A consumer is a subscription. AimDbHandle::consumer takes &self, the handle is Sync, and every call is a fresh subscribe_boxed with its own cursor: four threads created one each concurrently and all four saw every value, at 891ns per consumer. So the consumer half of a C ABI needs no core change. One ws_consumer* per thread, single-owner, the same ownership rule the header already states for ws_station_free. Two more things measured because they were the ones actually worth worrying about: sixteen threads parked in get() at once are all served, because Waiter::block_on drives on the calling thread rather than consuming a runtime worker; and Arc<Mutex<SyncConsumer>> already compiles for the genuinely shared case, since Mutex<T> is Sync whenever T is Send. Moving that Mutex inside SyncConsumer — what CR-6 asked for — would impose split semantics on the majority who want fan-out, and would break try_get's never-blocks contract. What is left is prose: say what a consumer is, and warn that replacing a 0.5.0 clone() with consumer() silently changes split into fan-out. That is the one way this change can break a working program with no compile error. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MRVG2B9A8hfX8JMiXsW6nD
DbErrorKind, eight kinds, each naming a different thing the caller does: Retry, Lagged, Closed, Transport, Data, Configuration, Usage, Internal. Eight rather than StationErrorKind's three because a database has more than three answers; the test is not how few but whether each kind is a distinct action. Lagged is separate from Retry because the caller lost data; Closed is separate because retrying cannot help. SyncError::kind() returns the same enum rather than one of its own, so an FFI layer has one switch for the whole stack. Db(e) delegates, timeouts are Retry, RuntimeShutdown is Closed. Both enums are now non_exhaustive, which follows a house style that already exists: RpcError, CodecError and AuthError in session/mod.rs are all non_exhaustive with small action-shaped variant sets. DbError and SyncError were the outliers. Both halves of the property verified rather than asserted. Adding a variant inside aimdb-core is E0004 at kind(), so someone must decide which action it belongs to in the file that owns it. A downstream match naming all 21 variants without a wildcard is also E0004, so reclassification cannot happen silently at a boundary. The blast radius is smaller than the variant count suggests: non_exhaustive blocks matching, not construction, and all 24 real match arms in the workspace are partial. aimdb-core, aimdb-sync, aimdb-tokio-adapter, aimdb-mqtt-connector, aimdb-data-contracts and the mesh workspace all build unchanged, and the spike stays green. The embassy and wasm adapters are not reachable from this workspace and were read, not built. Also exposed: AttachFailed carries a String, so it classifies as Internal even when the cause was a configuration mistake — which after the CR-2 patch is often exactly what its message says. Fixing it means carrying DbError through the startup channel instead of String. Left out because it changes a variant's shape, which is a decision rather than a cleanup. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MRVG2B9A8hfX8JMiXsW6nD
The only finding the spike measured as broken, and the only one whose fix had to be a process-global, which is why it belonged in the core rather than in the FFI layer. There were two failures, not one. The original round ended the child with _exit(0), so it never ran a destructor. Running one finds that joining a JoinHandle for a thread that does not exist in this process panics inside std with "threads should not terminate unexpectedly" — a Rust backtrace on stderr from inside ~Station. Present before this review's changes too. Detection is measured rather than assumed. try_set is 121ns and std::process::id() is 321ns, so a pid check per publish would cost more than twice the work it guards. Detection is a pthread_atfork child handler and the hot-path check is a relaxed atomic load; re-measured after the change, try_set is 107ns and the guard does not show up. A generation counter rather than a flag, because a bool would poison the child permanently — including for a database the child itself attaches afterwards, which a supervisor that forks per job would hit for no reason. The guard sits before the Weak upgrade deliberately: a forked child's upgrade succeeds, since the Arc was copied with the address space, and that is exactly why the buffer accepts a value nobody will read. Producers, consumers and the factories refuse a stale generation with a new SyncError::ForkedChild; detach and Drop release the join handle instead of joining; StationHandle::is_closed reports closed in a child. The spike's three notes are now four checks. CR-5 paid for itself here: ForkedChild is a new variant, which before this review would have been breaking. SyncError is non_exhaustive as of that work, so it is additive. fork::generation and fork::forked_since are public, which was not the plan. is_closed must not take the mutex shutdown holds, so it cannot ask the AimDbHandle whether the process forked — it records the generation itself. Any FFI layer on aimdb-sync has the same problem for the same reason. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MRVG2B9A8hfX8JMiXsW6nD
rumqttc's own default is use-rustls. Both aimdb-mqtt-connector and weather-station set default-features = false and then explicitly opted into use-native-tls, so the system OpenSSL was chosen over the pure-Rust default. The embedded half already went the other way — design 044's Embassy client is embedded-tls with rustpki — so the host path was the outlier. Three states now, not two: tokio-native-tls, tokio-rustls, or neither. Neither is a genuine choice, because a deployment speaking mqtt:// on a trusted network should link no TLS stack at all; mqtts:// then fails at connect time with a message naming the missing feature. That third state was the real work: rumqttc gates TlsConfiguration and Transport::Tls on having a backend, so the whole mqtts branch has to be cfg'd, not just its argument. The choice lives in weather-station and is passed down, because the pre-flight probe and the connector's data path must not disagree — two backends in one process is two TLS stacks, and a pre-flight that succeeded where the data path failed would defeat the point of probing. weather-station's default is now tokio-runtime + native-tls, so every existing station builds as before; weather-station-cpp, the crate that actually ships as a shared library, takes rustls. Measured on the cdylib: libssl.so.3 and libcrypto.so.3 are gone, and the static link line drops -lssl -lcrypto. It costs size — 3.9 MB to 8.1 MB release, 2.9 MB to 6.6 MB stripped — because rustls links the stack it no longer borrows. That belongs in the decision, not in a footnote. Found on the way: TlsConfiguration::default() expects and unwraps while loading platform certs. Two panics on the connect path, in a crate reached through an FFI boundary where a panic is undefined behaviour. Both backends now build the configuration explicitly. CR-4's rule has to extend to what the library calls, not just to what it writes. make test and make clippy carry all the combinations now — this repository's convention is that the feature matrix lives in the Makefile, and a TLS backend is exactly the kind of choice that rots if CI only ever sees one side of it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MRVG2B9A8hfX8JMiXsW6nD
These were cut to carry core changes that this repository cannot push. They are being raised as PRs against aimdb instead, so the copies here would only drift. review.md and REQUIREMENTS.md stay: they hold the measurements and the CR-n numbering the PRs are derived from. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… (design 050 §10.2) The C ABI door reached `tracing_subscriber::registry().…try_init()` from inside a cdylib loaded into somebody else's process — deciding where that process's diagnostics go, which is the one thing an FFI layer must not do. It did so because a `tracing::Layer` has nowhere to put the caller's `void *user_data`, and that single missing place is where both of this door's defects came from. `ws_init_logging` now builds a `CSink` — callback, `user_data` and filter in one value — and hands it to `log::set_boxed_logger`. The context travels with the callback, and the first-wins decision is made once, in Rust, for every binding. The header's `detail::SinkHolder` and `detail::sink_holder()` are gone with it. Both defects were that static: - it was assigned on every call, *before* asking whether the install would be accepted — last-wins over a first-wins C layer, so a second caller replaced the first caller's sink and returned false to say it had not; - it was assigned from the calling thread while the trampoline read it from aimdb's runtime thread. `init_logging` now allocates the `(sink, user_data)` pair the C ABI cannot carry in one `void *`, hands it over as `user_data`, and releases it if the install is refused. The trampoline stays — it is what catches a C++ exception that would otherwise unwind into Rust, which is a separate concern from the static and still load-bearing — but it holds nothing. Measured by running the current spike against the previous header: 86,890 events went to the sink that had just been told it was not installed, and the first caller's sink stopped receiving entirely. Against this one, zero. Two new rounds in spike.cpp assert both halves, and the second is written so it cannot pass vacuously. `weather-station` gains a `log` feature mirroring its `tracing` one — required, not cosmetic: the `log_*` macros expand in that crate, so the arm is selected by its features, and without it every call site there also warned under `unexpected_cfgs`. `init_tracing` and `tracing-subscriber` move out of `tokio-runtime` into a new `init-tracing` feature, which `default` carries, so the station binaries and templates are unchanged while a library build can have the Tokio station without a subscriber. `cargo tree` for this door now contains neither `tracing-subscriber` nor `tracing`. Filtering is coarser, deliberately: `EnvFilter` came free with `tracing-subscriber` and went with it. `filter` now takes `level` and `target=level`, comma-separated, longest prefix first. Named as a regression in README.md rather than left to be discovered. Verified: `make clippy`, `make test`, `make fmt-check` clean; the spike's log rounds all pass and still report `aimdb_core::builder` / `aimdb_core::session::pump` targets. The five broker-path failures it reports are pre-existing — identical on this branch before this change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QgaHbjCPgkNS6Y34jxqVYH
… (design 050 §10.3)
Removing `init_tracing` from this module was meant to end one trespass: an
extension module is a library inside somebody else's application, and
process-wide logging is that application's decision. The replacement left half
of it standing. Reaching a `tracing::Layer` at all meant calling
`tracing_subscriber::registry().…try_init()`, so `init_logging` installed the
interpreter's global subscriber — the same trespass, one layer further down and
harder to see.
`PyLogger` is a `log::Log` instead: an ordinary destination this module owns.
Nothing global is installed on the application's behalf beyond the one logger
`log` exists to hold, and `log::set_boxed_logger` is what makes the `True`/
`False` honest — the first-wins decision is now made once, in `log`, for this
door and the C one alike rather than separately in each.
Everything Python-visible is unchanged: events still arrive on loggers named
after the emitting Rust module with `::` translated to `.`, so
`getLogger("aimdb_core").setLevel(...)` still silences aimdb without silencing
the station. The `MessageVisitor` goes with `tracing` — a `log::Record` carries
its message as `args()` and needs no field visitor.
`weather-station` gains a `log` feature mirroring its `tracing` one — required,
not cosmetic: the `log_*` macros expand in that crate, so the arm is selected by
its features. `init_tracing` and `tracing-subscriber` move out of
`tokio-runtime` into a new `init-tracing` feature that `default` carries, so the
station binaries and templates are unchanged while this crate can take the Tokio
station with `default-features = false` and get neither. `cargo tree -p
weather-station-py` now contains neither `tracing-subscriber` nor `tracing`.
Filtering is coarser, deliberately: `EnvFilter` came free with
`tracing-subscriber` and went with it. `filter` now takes `level` and
`target=level`, comma-separated, longest prefix first, in either spelling —
directive prefixes are normalised to `::` at construction, so a Python caller
can write the dotted logger names this bridge hands out and still match, without
paying for a translation per event on the path the gate exists to keep cheap.
It costs less here than at the C door: `filter` is only the floor that keeps
events from acquiring the GIL, and Python's own per-logger levels do the
fine-grained work above it as they always did. Named as a regression in
README.md rather than left to be discovered.
Verified: `make clippy`, `make test`, `make fmt-check` clean, and `make spike`
reports all checks passed against a live broker — including "the module does not
install a global tracing subscriber", which is now true by construction rather
than by convention, and "one subsystem can be silenced without silencing the
others".
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QgaHbjCPgkNS6Y34jxqVYH
This branch is nine commits behind `weather-station-cpp`, and one of them — `fafe259` (#10) — tightened the doc comments throughout `weather-station-py/src/lib.rs`. The previous commit was written against the older text, so merging it would have reverted that work in every block it touched, and left the blocks it did not touch looking like a revert too. Re-applied the design-050 change on top of #10's version of the file instead. No behaviour change from the previous commit: `PyLogger` and `init_logging` are the same, and the module-doc note about the bridge is rewritten in #10's register rather than the older one. The diff against #10 is now confined to the imports, the module-doc tail and the bridge itself — `PyStation` is untouched. #10's change to this crate was comment-only, so nothing here depended on it. `make spike` still reports all checks passed; `make clippy`, `make test` and `make fmt-check` clean. Note for whoever merges: this branch is still missing the other eight commits on `weather-station-cpp`, including the C door itself. Which way those branches should converge is a topology decision, not one this commit makes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QgaHbjCPgkNS6Y34jxqVYH
Brings the design-050 migration of the pyo3 door onto the branch that carries both. `weather-station-cpp` was nine commits ahead, so its copy of the Python door was still installing the interpreter's global `tracing` subscriber while its `weather-station` had already moved to the `log` facade. Three conflicts, all in files both sides edited: - `weather-station-py/src/lib.rs` — cpp's side was #10's doc tightening, which the py branch had already been rebuilt on top of, so the migrated version is a strict superset. Took it whole; the result is byte-identical to the py branch's file. - `weather-station/Cargo.toml`, `default` — kept `native-tls`. The py branch predates CR-11 and had no TLS backend to name; dropping it here would have left the default build without one. - the same file's `log` feature comment — one word apart; kept "destination", which is the design's vocabulary. One thing neither branch could have got right alone: `weather-station-py` takes `weather-station` with `default-features = false`, and on the py branch that was harmless because a TLS backend was not yet selectable — `rumqttc` named `use-native-tls` directly. Merged, it means no backend is selected at all and `mqtts://` would have failed at run time with nothing failing at build time. Named `rustls` explicitly, for the reason CR-11 gives and which describes this crate exactly: a build that must not hand its host process a second OpenSSL — "an FFI layer shipped as a shared library, above all". The C door made the same call for the same reason. `cargo tree -p weather-station-py` links no OpenSSL (`openssl-probe` is `rustls-native-certs`' cert-store locator, not a link to OpenSSL) and still contains neither `tracing` nor `tracing-subscriber`. That is the one judgement call in this merge rather than a mechanical resolution; `native-tls` is a one-word change if the wheel should keep it. Verified on the merged tree: `make clippy`, `make test`, `make fmt-check` clean; `make spike` and `make spike-cpp` both report all checks passed against a live broker. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QgaHbjCPgkNS6Y34jxqVYH
`make clippy` fails on rustc 1.98 with `clippy::unnecessary_sort_by`: `sort_by(|a, b| b.0.len().cmp(&a.0.len()))` is a reversed comparison that `sort_by_key` with `Reverse` expresses directly. Same descending order, same stable sort — the longest-prefix-first invariant both filters rely on is unchanged. Both doors carried the identical line; CI only reported the Python one because `make clippy` stops at the first failing package, so the C door's copy was never reached. Fixed in both rather than one at a time. Why local checks missed it: this sandbox was on rustc 1.94.1, where the lint does not fire on a reversed comparison, while CI runs 1.98.0. Toolchain updated to 1.98.0 here, the failure reproduced against the old code, and the whole suite re-run on it — `make clippy`, `make test`, `make fmt-check`, `make lockfile` clean, and `make spike` / `make spike-cpp` both report all checks passed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QgaHbjCPgkNS6Y34jxqVYH
Member
Author
|
Closing in favour of #12, which continues this branch: Nothing is lost by closing this one. Generated by Claude Code |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Brings both FFI spikes onto
main: the pyo3 door, the C ABI door, the review fixes that came out of them, and the design-050 migration that stops either one installing its host application's globaltracingsubscriber.Dependency on aimdb — resolved
This needed
aimdb-core'slogfeature, which was not on aimdb'smainwhen this PR was opened. It is now, as aimdb#237 (878486b)..github/workflows/ci.ymlchecks outaimdb-dev/aimdbwith noref:, so CI picks it up automatically — the first run here predated the merge and failed withaimdb-core does not have that feature; it has been re-run against the merged base.What design 050 changes, and why
Both doors had arrived at the same unhappy answer to "how does a non-Rust application receive aimdb's reporting?": add
tracing-subscriber, write aLayer, and install the host process's global subscriber on its behalf. That is the one thing a library loaded into somebody else's process must not do — and for the Python door it was the same trespass that removinginit_tracingwas meant to end, committed one layer further down where it was harder to see.aimdb-coregrew a second, optional destination for itslog_*facade, so a door can hand aimdb a destination it owns instead. Both doors now install alog::Log; neither carriestracing-subscriberany more.It deletes two real defects in the C++ header
A
tracing::Layerhas nowhere to put avoid *user_data, soweather_station.hppkept adetail::SinkHolderstatic beside the callback. Everything wrong with it followed from that:init_loggingreplaced the first caller's sink and returnedfalseto say it had not;A
log::Logimpl is the context, so the static has no reason to exist.ws_init_loggingforwards tolog::set_boxed_logger, which makes the first-wins decision once and in Rust for every binding; the header allocates the(sink, user_data)pair the C ABI cannot carry in onevoid *, hands it over asuser_data, and releases it if the install is refused.Measured by compiling the current spike against the previous header: 86,890 events went to the sink that had just been told it was not installed, and the first caller's sink stopped receiving entirely — taking the reentrancy and target-routing rounds down with it. Against this header, zero. Two new spike rounds assert both halves, and the second is written so it cannot pass vacuously.
The trampoline itself stays. It does two jobs — carry
user_data, and catch a C++ exception that would otherwise unwind into Rust — and only the first is obsolete. It now holds no state.Feature layout
weather-stationgains alogfeature mirroring itstracingone. This is required rather than cosmetic: thelog_*macros are#[macro_export]ed, so#[cfg(feature = "log")]inside them resolves against the expanding crate. Without it, that crate's call sites are silently unrouted — and, since Cargo'scheck-cfg, also warn once per call site.init_tracingandtracing-subscribermove out oftokio-runtimeinto a newinit-tracingfeature thatdefaultcarries. Station binaries and templates are unchanged; a library build can now have the Tokio station without a subscriber, which is what makes "notracing-subscriberincargo tree" reachable at all.One judgement call worth a reviewer's eye
weather-station-pytakesweather-stationwithdefault-features = false. On its own branch that was harmless, because a TLS backend was not yet selectable —rumqttcnameduse-native-tlsdirectly. Merged with CR-11's backend selection, it means no backend is selected at all, andmqtts://would have failed at run time with nothing failing at build time. A clean auto-merge of two correct sides producing a broken result.Resolved by naming
rustlsexplicitly, for the reason CR-11 itself gives and which describes this crate exactly — a build that must not hand its host process a second OpenSSL, "an FFI layer shipped as a shared library, above all". The C door made the same call.cargo tree -p weather-station-pylinks no OpenSSL; the oneopenssl-probein the graph isrustls-native-certs' cert-store locator.native-tlsis a one-word change if the wheel should keep it.Known regression, named rather than left to be discovered
Filtering is coarser on both doors.
EnvFiltercame free withtracing-subscriberand went with it; span, field and regex directives are gone. What remains islevelandtarget=level, comma-separated, longest matching prefix first.It costs little at the Python door, where
filteris only the floor that keeps events from acquiring the GIL and Python's own per-logger levels do the fine-grained work above it. It costs more at the C door, where it is the only filter there is. Both READMEs say so. No Rust consumer is affected — they keeptracingandEnvFilterexactly as today.Verification
Re-run locally against aimdb
mainat878486b(not the feature branch), with a live broker:make clippy,make test,make fmt-check,make lockfile— cleanmake spike— all checks passed, including "the module does not install a global tracing subscriber" (now true by construction rather than by convention) and "one subsystem can be silenced without silencing the others"make spike-cpp— all checks passed, including the two new rounds above, and the sink still reportsaimdb_core::builder/aimdb_core::session::pumptargetscargo treefor both doors contains neithertracingnortracing-subscriber.Note on the branch
weather-station-pyis fully contained in this branch and can be deleted once this merges.