You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* feat(data-contracts): add SimulatableRegistrarExt and ObservableRegistrarExt for enhanced data integration
* Implement code changes to enhance functionality and improve performance
* feat(data-contracts): Simulatable v2 — compile-time-only sim + .simulate() 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>
* Refactor observability features in AimDB
- 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.
* feat(data-contracts): LinkableRegistrarExt + #[derive(Linkable)] (design 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>
* feat(sync): Settable -> SyncProducer::set_value family (design 041 §3.4)
`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>
* docs: README verb/tier table + CHANGELOG sweep + version bumps (design 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>
* Claude/design doc 41 review s7hguj (#169)
* 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>
* feat(tests): add serde_json as a dev-dependency for roundtrip tests
* Refactor documentation and comments across multiple modules
- 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.
* docs: update next development areas section to reference GitHub issues
* feat(docs): enhance README with evidence of data contracts and capabilities
* refactor(examples): simplify comments in main.rs for clarity
* feat(observable): enhance documentation on feature layering and recording behavior
* feat(error): improve server error message formatting
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
printf"$(RED)✗ positive control failed: '$$bin --features sim' does not pull 'rand' — the tracer no longer traces, so the rand-free results above prove nothing$(NC)\n";\
Copy file name to clipboardExpand all lines: README.md
+52-24Lines changed: 52 additions & 24 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -39,12 +39,22 @@ AimDB is not a storage engine. It's a typed data plane where the Rust type *is*
39
39
40
40
## Why AimDB exists
41
41
42
-
Distributed systems spend most of their complexity budget translating between layers. IDLs, codegen, serialization, schema registries and glue services. AimDB removes that layer by making **the Rust type the contract**: defined once, compiled unchanged from a `no_std` microcontroller to the browser.
42
+
Most of a distributed system's complexity is translation: IDLs, codegen, serialization glue, schema registries. AimDB deletes that layer by making **the Rust type the contract** — defined once, compiled unchanged from a `no_std` microcontroller to the browser.
43
43
44
44
-**One type, every tier.** The same struct compiles for firmware and cloud. No conversion layer between them.
45
45
-**The buffer defines how data moves.** No manual queue wiring, no separate transport config.
46
46
-**No untyped boundaries.** Capabilities, like streaming, migration, observability and connectors, are unlocked by traits.
47
47
48
+
### The receipts
49
+
50
+
None of this is roadmap. Every claim is backed by code, tests or committed benchmarks:
51
+
52
+
-**Data contracts as schema** → the Rust type *is* the wire contract. No IDL, no codegen, [CI cross-compiles](Makefile) unchanged from Cortex-M to WASM.
53
+
-**Typed data migrations** → [`migration_chain!`](aimdb-data-contracts/src/migratable.rs) const-validates the chain and works `no_std`, so old and new nodes coexist on the wire.
54
+
-**Zero allocation per message** → [allocation baselines](aimdb-bench/data/baselines) across Tokio, Embassy and WASM.
55
+
-**Non-blocking producers** → synchronous [`produce()`](aimdb-core/src/typed_api.rs) calls and overwrite semantics on all buffers.
56
+
-**Identical buffer contracts across runtimes** → [shared conformance suite](aimdb-core/src/buffer/test_support.rs) validates SPMC Ring, SingleLatest and Mailbox on every adapter.
57
+
48
58
[The Next Era of Software Architecture Is Data-First](https://aimdb.dev/blog/data-driven-design)
**One async API across runtimes.**Tokio, Embassy, WASM — swap the runtime adapter, keep the code. → [How the runtime abstraction works](https://aimdb.dev/blog/building-aimdb-one-async-api)
**Connectors that ship today:** MQTT, KNX, WebSocket, TCP, serial (COBS-framed UART) and Unix domain sockets. Writing your own is one trait impl. → [Connector status](#connectors)
139
141
140
-
**One async API across runtimes.** Tokio, Embassy, WASM — swap the runtime adapter, keep the code. → [How the runtime abstraction works](https://aimdb.dev/blog/building-aimdb-one-async-api)
142
+
**Optional persistence.** The core is an in-memory data plane; [`aimdb-persistence`](aimdb-persistence) adds `.persist()` with a SQLite backend ([`aimdb-persistence-sqlite`](aimdb-persistence-sqlite)) for records whose history must survive restarts.
143
+
144
+
Deep dives: [source/tap/transform](https://aimdb.dev/blog/source-tap-transform) · [schema migration](https://aimdb.dev/blog/schema-migration-without-ceremony) · [reactive pipelines](https://aimdb.dev/blog/reactive-pipelines)
145
+
146
+
---
141
147
142
-
**Connectors that ship today:** MQTT, KNX, WebSocket. Writing your own is one trait impl.
|[`Linkable`](https://aimdb.dev/blog/connectors-where-aimdb-meets-the-real-world)| the record is mirrored to/from an endpoint (MQTT, KNX, serial, UDS…) |`.linked_from(url)` / `.linked_to(url)` (`#[derive(Linkable)]` for JSON) | wire (prod) |
155
+
|[`Streamable`](https://aimdb.dev/blog/streamable-crossing-boundaries)| the record streams to browsers as schema-named JSON | ws-connector `.register::<T>()`| wire (prod) |
156
+
|[`Migratable`](https://aimdb.dev/blog/schema-migration-without-ceremony)| the schema evolved across versions |`migration_chain!`| wire (prod) |
157
+
|`Settable`| sync code outside AimDB sets the value |`SyncProducer::set_value(v)`| wire (prod) |
158
+
|`Observable`| the value is worth watching in production |`.observe()` → live last/min/max/mean on `record.list`/`record.get`| introspection (prod, optional) |
159
+
|`Simulatable`| the type can generate realistic synthetic data |`.simulate(profile, rng)`|**dev-only — never ships in prod**|
160
+
161
+
`Simulatable` is the odd one out: it lives behind the `simulatable` feature (never a default) and switching from simulated to real data is one `#[cfg]` in your app:
162
+
163
+
```rust
164
+
builder.configure::<Temperature>(KEY, |reg| {
165
+
#[cfg(feature ="sim")]
166
+
reg.simulate(profile, rng);
167
+
#[cfg(not(feature ="sim"))]
168
+
reg.source(read_hardware);
169
+
});
170
+
```
171
+
172
+
Build with `sim` off and the simulation code is *gone* — no `T::simulate` impls, no `rand`, nothing to audit.
173
+
174
+
**Old and new nodes coexist.** Migration steps are typed and bidirectional and `migration_chain!` checks the whole chain at compile time — [roundtrip tests](aimdb-data-contracts/tests/migration_roundtrip.rs) cover upgrade *and* downgrade across multi-step chains. It's `no_std` too: even an MCU can accept a payload one schema version behind or downgrade its output for an older peer.
175
+
176
+
Deep dive: [data contracts](https://aimdb.dev/blog/data-contracts-deep-dive)
145
177
146
178
---
147
179
@@ -157,10 +189,10 @@ A record is written by a `Source`, lands in a typed `Buffer` and fans out to in-
└──────────────┘ ───► Link ──► MQTT / KNX / WebSocket
192
+
└──────────────┘ ───► Link ──► MQTT / KNX / WS / UDS / serial
161
193
```
162
194
163
-
The Rust type system enforces correctness at compile time, buffer semantics enforce flow guarantees at runtime and connectors wire to your infrastructure without an integration layer. The same code compiles for MCU, edge, cloud or browser — see [Platform Support](#platform-support) below.
195
+
Types check correctness at compile time, buffers enforce flow semantics at runtime and connectors bridge to your infrastructure, with no integration layer in between. The same code compiles for MCU, edge, cloud or browser — see [Platform Support](#platform-support) below.
164
196
165
197
---
166
198
@@ -211,6 +243,8 @@ See the [MCP server docs](tools/aimdb-mcp/) for Claude Desktop and other editors
211
243
|**Kafka**| 📋 Planned | std |
212
244
|**Modbus**| 📋 Planned | std, no_std |
213
245
246
+
The serial, TCP and UDS connectors carry both record mirroring and the AimX remote-access protocol used by the CLI and MCP server. Typed records over multiple transports, from bare metal to cloud.
247
+
214
248
---
215
249
216
250
### Platform Support
@@ -228,14 +262,8 @@ See the [MCP server docs](tools/aimdb-mcp/) for Claude Desktop and other editors
228
262
229
263
We're a small team building something ambitious. The fastest way to help is to take on a scoped piece of it. Each of these is sized for a few hours and includes file pointers, acceptance criteria and a place to ask questions:
230
264
231
-
-[#92 — `no_std``Display` for `DbError` should include numeric fields](https://github.com/aimdb-dev/aimdb/issues/92) · 2–3h · core · embedded
Copy file name to clipboardExpand all lines: aimdb-core/CHANGELOG.md
+6-1Lines changed: 6 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
8
8
## [Unreleased]
9
9
10
+
### Added
11
+
12
+
-**`RecordRegistrar::signal_gauge(name, unit) -> SignalGaugeHandle`** (design 041 §3.2) — the core hook behind `aimdb-data-contracts`'s `Observable::observe()`. Feeding values via `SignalGaugeHandle::update(f64)` folds them into per-record `SignalStats` (last/min/max/mean), surfaced through `RecordMetadata::signal_stats` on `record.list`/`record.get`. Mirrors the `with_name` precedent: always callable, an inert no-op handle when the `observability` feature is off, so callers never `#[cfg]` on core's features. New public types: `SignalGaugeHandle` (always available), `SignalStats`/`SignalGauge`/`SignalStatsInfo` (feature `observability`).
13
+
10
14
### Fixed
11
15
12
16
-**AimX protocol doc rot cleaned up; `remote::PROTOCOL_VERSION` corrected to `"2.0"` and exported.** The `remote` module docs claimed "AimX v1" and linked a spec file that no longer exists; they now describe the v2 NDJSON tagged-frame wire and point at `crate::session::aimx` / `docs/design/remote-access-via-connectors.md`. The AimX dispatch's Welcome uses the constant instead of a hardcoded `"2.0"` (same bytes on the wire). The dead, never-exported v1 `Message` untagged envelope and its helpers were removed from `remote::protocol`. Also de-advertised Kafka/HTTP connector semantics from `ConnectorUrl` docs (the parser is scheme-agnostic; those connectors never existed) and updated the `connector` module docs from the removed `.link()` API to `.link_to()`/`.link_from()`.
@@ -452,7 +456,8 @@ warning fires if you exceed 1000 interned keys.
0 commit comments