Adds the ninth fleet-deployment crate. Default build is ROS2-free: topic_name
hex-escapes arbitrary tines topics into valid ROS2 names under /tines/ (deterministic,
injective) and wires Node<LogStore, BtTransport>. The rclrs-backed self-contained
TinesNode lives under #[cfg(feature="ros2")] with an off-by-default ros2 feature, so
the crate builds/tests without a ROS2 toolchain. Adds scripts/check-ros2.sh (needs a
ROS2 toolchain; not run in CI), registers the crate in workspace members, and passes
sh scripts/ci.sh.
Task 8 of the fleet-deployment plan. Adds the tines-bt crate: a real
`Transport` over the `bluer` crate (D-Bus BlueZ).
- `Addr` = peer's advertised `NodeId`, so identity survives BLE
address rotation; advertising payload (flags + 16-bit service UUID +
NodeId in service data) fits legacy 31-byte EIR (`advertise()`).
- `BtTransport` drops any inbound frame whose `from` differs from the
connection's advertised NodeId (protocol violation at the transport;
core never sees a mismatch).
- Connection symmetry: the node with the numerically smaller NodeId is
the BLE central/initiator (`is_central`), independent of which side
opens the radio.
- `mtu()` reports the real L2CAP CoC MTU (falling back to the ATT MTU);
the transport does NOT split frames — core folds summaries/fragments
to `mtu()`.
- Radio I/O sits behind the narrow `Radio` trait (a `poll`-driven event
stream), so the pure logic (payload assembly, mismatch-drop, central
rule) is unit-tested with a mock radio and never needs hardware.
- `BluerRadio` drives the stack on a detached tokio task: powers the
adapter, advertises, monitors for the service UUID, opens an L2CAP
byte pipe, and surfaces inbound bytes tagged with the peer NodeId.
Adds `tests/adv.rs` (8 mock-Radio integration tests covering the 31-byte
limit, NodeId round-trip, MTU clamp, mismatch-drop, per-frame MTU, and
central rule). Added to workspace members; workspace CI (fmt, doc,
test, clippy -D warnings) is green.
Add the host-side std `tines-store` crate implementing the `tines_core::Store`
trait over a durable, append-only single-file log so a robot's memory survives
reboot.
- Log format: length-prefixed `[u32 LE][postcard]` records (Obs/Trim/Meta).
- On load the index is rebuilt by replaying the log, then every age is
rebased to load time: elapsed time since the last persisted clock marker is
folded into `age_ms` and `stored_at` reset to load `now`, so ages continue
instead of freezing across a reboot.
- Persisted state: released-id set, `next_seq`, and a clock marker in a `Meta`
record; dedupe to the oldest Obs and let the last event per id win so a
re-inserted id survives a trim.
- Implements `next_seq` and `flush`; `trim` writes Trim records best-effort.
- Registered as a workspace member; std-only, `#![forbid(unsafe_code)]`.
- Adds persistence tests (append durability, rebase-on-load, released/seq
persistence). Full gate (fmt, clippy, docs, tests, no_std thumb build) is green.
Share version, edition, license, authors, repository and readme through
[workspace.package]; add per-crate documentation, keywords and
categories. Fixes `cargo publish` warning about missing documentation,
homepage or repository.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bump tines-core and tines-sim to 0.2.0 (Peer::in_flight changed type).
Add CHANGELOG.md. README gains a "Writing a store" section and a note on
calling Exchange::on_send_error when driving Exchange without Node.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>