feat/advanced-weather-mesh#173
Closed
lxsaah wants to merge 35 commits into
Closed
Conversation
…trarExt for enhanced data integration
…ate() verb (design 041 §3.1) Reshape Simulatable to the one-verb-per-contract model and make simulation a compile-time fact, not a runtime flag: - Trait: add `type Params`; `simulate(params, previous, rng, timestamp_ms)`. Delete `SimulationConfig` (incl. `enabled`) and `SimulationParams`; add `SimProfile<P>` + off-the-shelf `RandomWalkParams`. - New `SimulatableRegistrarExt::simulate(profile, rng)` installs a source loop over `ctx.time()` (runtime-neutral, single-writer enforced by build()). - Cargo: `simulatable = ["rand", "aimdb-core"]`; `rand` drops `std_rng` (caller-supplied RNG; SmallRng needs no feature). D1 callers: - weather-mesh-common temp/humidity/location impls → `type Params = RandomWalkParams`. - weather-station-beta (std) + gamma (no_std): `sim` feature gates `.simulate()`; default (prod/flash) build reads a hardware source and is rand-free — the canonical §3.1.4 `#[cfg]` sim-to-real pattern. CI: `make check-no-sim` proves each station's production graph carries no `rand` (the tracer) and that `simulatable` is never a default feature; wired into the CI makefile-build job. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Removed `ICON` and `format_log` from the `Observable` trait, simplifying the interface. - Introduced `SignalGaugeHandle` for managing signal statistics, allowing for last/min/max/mean tracking. - Added `SignalStats` struct to encapsulate signal statistics with atomic operations for thread safety. - Updated `RecordProfilingMetrics` to include signal gauges and their statistics. - Enhanced `RecordMetadata` to optionally include signal statistics when the observability feature is enabled. - Modified `ObservableRegistrarExt` to support signal observation and logging. - Updated various schemas (e.g., `Temperature`, `Humidity`, `DewPoint`, `GpsLocation`) to align with the new observability model. - Adjusted example applications to utilize the new signal gauge functionality for metrics and logging.
…ign 041 §3.3) Implementing `Linkable` today changed nothing — every example hand-wired `with_deserializer`/`with_serializer` closures. Fix: - `LinkableRegistrarExt::linked_from`/`linked_to` install `.link_from()`/ `.link_to()` with the codec defaulted to `T::from_bytes`/`T::to_bytes`; the raw builders remain the escape hatch for per-link options (QoS, topic providers/resolvers). - `linkable` feature now depends on `aimdb-core` (same wiring as `observable`) for the registrar/connector-builder types the ext needs. - `#[derive(Linkable)]` in `aimdb-derive` emits `serde_json::to_vec`/ `from_slice` — no_std + alloc compatible (serde_json here is alloc-only, no `std` required), removing hand-written JSON boilerplate. D1 caller: tokio-mqtt-connector-demo's TempOutdoor/TempServerRoom (outbound) and CommandKey::TempOutdoor (inbound) now use `#[derive(Linkable)]` + `.linked_to`/`.linked_from`; TempIndoor keeps the raw builder since it needs QoS/retain options the ext doesn't cover. mqtt-connector-demo-common gains an opt-in `data-contracts` feature (alloc-only) so the embassy demo's hand-rolled no_std JSON path stays untouched — it doesn't enable the new feature. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`Settable` was built for the sync bridge, but `aimdb-sync` never referenced it — `SyncProducer::set(value: T)` takes a fully constructed record, so every outside-the-thread caller hand-assembled the struct. - `Settable` moves behind a `settable` feature in `aimdb-data-contracts` for tier symmetry with the other wire contracts (pure trait, no dependencies of its own). weather-mesh-common enables it unconditionally (used by all three contracts); codegen's `generate_cargo_toml`/import emission gained the same `has_settable` detection already used for `has_observable`. - `aimdb-sync` gains a `data-contracts` feature -> optional `aimdb-data-contracts/settable` dep (dependency direction stays contracts -> sync, no `sync` feature added to the contracts crate). `SyncProducer<T: Settable>::set_value`/`try_set_value`/`set_value_at` construct via `T::set(value, timestamp)` and send; `set_value`/ `try_set_value` stamp the caller's `SystemTime`, `set_value_at` takes an explicit timestamp for replay/testing. - Doctest + integration tests (aimdb-sync/tests/settable_integration.rs) exercise `set_value`/`try_set_value`/`set_value_at` end-to-end (construct -> produce -> consume). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…n 041 §6-7) - README: the 4-trait capability table becomes the §2 verb/tier table (Contract / Implement when / Verb / Tier), documents the `Simulatable` dev-tier "never ships in prod" call-out with the `#[cfg]` sim-to-real pattern, and adds the `Settable` row. - CHANGELOG entries for all four touched crates (aimdb-data-contracts, aimdb-core, aimdb-derive, aimdb-sync) describing the trait reshapes, new registrar ext traits, the signal-gauge surface, and the derive macro. - Version bumps: aimdb-data-contracts 0.2.0 -> 0.3.0 (breaking: Simulatable/ Observable reshaped, Settable feature-gated), aimdb-core 1.1.0 -> 1.2.0 (additive: signal_gauge surface), aimdb-derive 0.2.0 -> 0.3.0 (additive: #[derive(Linkable)]), aimdb-sync 0.5.0 -> 0.6.0 (additive: set_value family). Dependents pinning an exact version (aimdb-sync, aimdb-wasm- adapter, aimdb-websocket-connector -> data-contracts; aimdb-core, aimdb-data-contracts -> aimdb-derive) updated to match; aimdb-core's own 0.x-style dependents needed no change (^1.1.0 already permits 1.2.0). Verified: `make check` and `make build` green in aimdb; `make check`/`make all` green in aimdb-pro against the bumped versions. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(review): SignalStatsInfo serde symmetry, live hub gauge, gamma fmt Review follow-ups on the design 041 implementation: - SignalStatsInfo.unit: add #[serde(default)] to match its skip_serializing_if. Observable::UNIT defaults to "", so a gauge without a unit serialized record.list/record.get JSON that aimdb-client/aimdb-mcp (built with observability) could not deserialize back (missing field `unit`). Regression test added. - weather-hub: enable aimdb-tokio-adapter/observability so .observe() gets a live gauge instead of the inert handle — the demo now actually surfaces signal stats on record.list/record.get as its comments and design 041 §6 claim. - weather-station-gamma: rustfmt the non-sim read_temperature signature (make fmt-check was failing). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017sJzkN61UVQboWuHVHwSKP * ci: make check-no-sim fail closed + tracer positive control The guard treated ANY non-zero 'cargo tree -i rand' exit as 'rand is absent'. An unrelated cargo failure — missing _external submodules, a typo'd package in SIM_EXAMPLES, registry trouble — therefore passed the check vacuously (observed: with submodules uninitialized, all four assertions 'passed' while cargo was erroring on embassy-executor). Now 'rand-free' is accepted only when cargo tree fails with its specific 'did not match any packages' error; any other failure aborts the check and prints cargo's output. A positive control per example additionally asserts the '--features sim' graph DOES find rand, so a silently broken tracer (e.g. rand dropped from the simulatable feature) can no longer turn the whole guard into a no-op. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017sJzkN61UVQboWuHVHwSKP --------- Co-authored-by: Claude <noreply@anthropic.com>
- Updated comments in `linkable.rs`, `observable.rs`, and `simulatable.rs` to improve clarity and remove references to design documents. - Enhanced documentation in `lib.rs` of `aimdb-derive` to clarify the derive functionality for `Linkable`. - Revised comments in `Cargo.toml` files across various examples to remove design references and improve readability. - Adjusted comments in `producer.rs` and integration tests to clarify the functionality of `SyncProducer` and its methods. - Updated design documentation to reflect changes in the implementation and clarify the purpose of various traits and features. - Removed unnecessary design references in example projects to streamline the documentation and focus on functionality.
Specifies how the weather-mesh demo becomes a public, joinable flagship instance: string-keyed slot pool in the hub (no core changes), EMQX Cloud broker with per-station credentials/ACLs, self-serve admission via GitHub device flow in a new generic 'aimdb join' CLI subcommand, an open provisioning endpoint format, privacy/retention policy, and the open Embassy TLS question. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFw7Pz4cjF6dUT9Zv2T8t9
Add §8.2 describing the hardware-station user flow: aimdb join on the flashing host, build-time config embedding via MESH_CONFIG in build.rs with local-demo fallback, and the trust model for credentials in the firmware image. Ground §8.1 option 1 with the concrete TLS work (TRNG entropy, SNTP time source) and update sequencing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFw7Pz4cjF6dUT9Zv2T8t9
…hip (#172) Translates the 042 design spec into sequenced work packages with file-level detail for the OSS items (hub slot pool, aimdb join, generic cloud station, gamma build-time config, Embassy TLS) and checklists for the external ones (EMQX Cloud, provisioning service, website). Claude-Session: https://claude.ai/code/session_012h2fegrHHQqRFT48dW8oZW Co-authored-by: Claude <noreply@anthropic.com>
ConnectorUrl parsing already recognised the mqtts scheme (port default 8883, URL-embedded credentials), but rumqttc defaults to plain TCP unless a TLS transport is set explicitly — an mqtts:// broker URL silently spoke cleartext to port 8883 and failed the handshake. Select Transport::Tls with the native-tls default configuration (system trust roots) when the scheme is mqtts, which the public weather mesh hub relies on to reach EMQX Cloud (design 042 §5). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
MESH_SLOTS switches the hub into public-mesh mode (design 042 §4): a bounded
pool of N string-keyed slot records (station.{slot}.temperature/humidity,
default 64) subscribed to station/{slot}/… topics, with DewPoint derived per
slot at the hub via transform_join so the dashboard stays supplied even when
a station publishes only temperature/humidity. Slot records exist from
startup, so admission assigns a slot number instead of requiring an enum
variant and a recompile.
MQTT_URL accepts a full connector URL (mqtts://hub-sub:…@…:8883 for the EMQX
Cloud deployment, credentials redacted from logs); unset, the hub falls back
to the local demo's host-only MQTT_BROKER variable. With MESH_SLOTS unset the
three-enum docker-compose demo configuration is unchanged.
Mesh mode drops the per-value console .log() lines (64 slots of per-message
logging is noise); .observe() signal gauges remain on record list/get.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The open contract for POST /v1/join that aimdb join is built against (design 042 §6.3): the auth.kind = github | claim-token identity envelope, the opaque deployment-specific app map, the profile_version-ed response with the scoped broker credential, the 403/409/503 error bodies whose message the CLI prints as-is, and the station.toml serialization consumed by weather-station and gamma's MESH_CONFIG build embedding. The server side stays in the private ops repo (042 D6); this doc keeps the format public so the CLI is deployment-agnostic and any AimDB deployment can implement admission. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New join subcommand speaking the open provisioning format of design 043:
aimdb join <provisioning-url> [--token <t>] [--out station.toml]
Default auth is the GitHub OAuth device flow (042 §6.1): print the user
code + verification URL, poll for the access token respecting
interval/slow_down, no scopes requested. The public client ID is compiled
in via AIMDB_GITHUB_CLIENT_ID at build time (nothing secret ships in the
CLI); without one the command points at --token. --token switches the
envelope to the claim-token auth kind for deployments without public
self-serve.
The deployment's app fields (v1: station name, city) are collected via
stdin prompts and passed through untouched — no deployment-specific types
in the CLI. On 403/409/503 the service's message is printed as-is; on
success the profile is written as TOML (mode 0600 — it carries the broker
credential) and the assigned station id + name are printed. The CLI never
talks to the broker management API (042 D6/D7).
Dependencies (reqwest with rustls, toml) are gated behind a new join cargo
feature, on by default. Unit tests cover profile JSON→TOML round-trip,
unknown-field tolerance (043 §5), error-body handling, file permissions,
and both paths against a one-shot mock HTTP server.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… WP5) New weather-station crate — the binary in the design's three-command demo loop: aimdb join writes station.toml, then cargo run -p weather-station -- --config station.toml. The profile (design 043 §4) supplies everything: broker URL + per-slot credential (embedded into the connector URL; redacted in logs), the assigned slot (station_id = slot-<n> → station.<n>.* records, station/<n>/… topics), the station name, and the service-coarsened lat/lon fed to the same Open-Meteo source as station alpha. DewPoint is derived on the station via transform_join exactly as alpha does. Failure/success UX per design §9/§8: a one-shot CONNECT pre-flight turns a revoked credential into a clear pointer back to aimdb join instead of the connector's silent retry-forever, and the first successful publish prints the station's live chart URL (aimdb.dev/mesh/<name>) plus a ready-made MCP prompt — the shareable moment. Alpha, beta, and the docker-compose demo are untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…042 WP6) build.rs reads the station profile at $MESH_CONFIG (the same station.toml that aimdb join writes, design 043 §4) and generates station_config.rs into OUT_DIR — broker host/port, credentials, slot, station name, and the slot-scoped station/<n>/… topics — so the MCU join path is the cloud-station flow with the flash step swapped in: MESH_CONFIG=../station.toml cargo run --release (design 042 §8.2). rerun-if-env-changed/rerun-if-changed keep the generated file honest across builds. With MESH_CONFIG unset the generated defaults are exactly the previous hardcoded consts (192.168.1.3:1883, sensors/gamma/… topics), keeping the tier-1 local demo zero-setup; the split build/flash flow (flash.sh) is untouched since MESH_CONFIG matters only at build time. main.rs include!s the generated config, keeps the compile-time enum record keys, and prints the mesh success banner (live chart URL + MCP prompt) over defmt. An mqtts:// profile fails the build with a pointer at design 042 §8.1 — the Embassy client has no TLS yet (WP7), so MESH_CONFIG firmware can only target a plain-TCP broker until then. The generated credential consts are in place for WP7 to consume. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…sh hub (042 WP1 follow-up) Verification of the local drill (plan §End-to-end #1) surfaced three gaps between the slot pool and the design's demo loop: - Mesh mode now registers a read-only AimX TcpServer (AIMX_BIND, default 127.0.0.1:7433) — without it there is no server behind 'aimdb record list --url tcp://…:7433' (design 042 §7). The local demo path is unchanged (no listener). - Slot records register .with_remote_access(), installing the JSON codec that record.get/subscribe and the dashboard read through. - The mesh deserializers report contract violations through ctx.log() — design §9 makes 'a malformed payload is visibly rejected at the hub' a feature, and the router's own error path is compiled out without the tracing feature. The fallback env filter now includes the runtime-context log target (aimdb=info) so the reports actually show on bare cargo run; the docker demo always sets RUST_LOG explicitly and is unaffected. Verified against mosquitto: MESH_SLOTS=4, publishes to station/2/… surface as station.2.temperature/humidity signal gauges over tcp://127.0.0.1:7433, the per-slot join derives station.2.dew_point (21.4°C/55% → 12.4°C), and a malformed payload logs 'station.2.temperature: rejected payload: Migration error: …' while the slot keeps its last good value. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…follow-up) The AimX server attaches signal_stats/stage_profiling/buffer metrics to record.list when built with observability, but those RecordMetadata fields are themselves cfg-gated — a client compiled without the feature silently drops them on deserialize, so 'record list --format json' showed no signal gauges even when the server sent them. Enable aimdb-core/observability for the CLI so the fields round-trip; this is also how the mesh drill confirms a slot is updating (ring buffers have no canonical latest for record.get). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…low-up) Returning Err from main Debug-formats the message — the revoked-slot text came out as one line of escaped \n. Route run() errors through eprintln (Display) + exit(1) so the design §9 failure UX reads as written. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…usage Plan 042 status now reflects the implemented OSS work packages (WP1, WP3's 043 doc, WP4, WP5, WP6 and the verification follow-ups); the demo README documents the opt-in public-mesh mode (MESH_SLOTS/MQTT_URL/AIMX_BIND, the weather-station join loop, gamma's MESH_CONFIG build embedding) alongside the unchanged local demo. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
reqwest's rustls-tls feature bundles webpki-roots, whose CDLA-Permissive-2.0 license is not on the deny.toml allowlist — make check failed at cargo-deny. Switch to rustls-tls-native-roots (system trust store), which also matches how TLS trust works everywhere else in the workspace. cargo deny is green again; the join unit/mock tests are unaffected. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-d52px3' into claude/new-vertical-hosting-plan-d52px3
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.
No description provided.