Commit Graph
100 Commits
Author SHA1 Message Date
kyleandClaude Opus 5.5 97a97379e2 fix(core): free drop-table keys on drain; summary rate-limit slack
- The drop table now empties on drain, keys included. Before, after 32
  distinct (addr, reason) pairs every later drop went to the overflow
  slot until reboot. The overflow slot's reason is documented as that of
  the first drop to overflow since the last drain (review S7).
- F9's receive-side check accepts a summary at least
  SUMMARY_INTERVAL_MS / 2 after the last one acted on, so an on-time
  summary measured at 1999 ms isn't ignored (decision L, review D-f).
- DropReason::IdentityChanged loses its variant-level non_exhaustive,
  which made it unmatchable outside the crate; the enum keeps its own.
- Stale comments: drain_events is used by Node (drop the dead_code
  allow), and NodeEvent drops are this node's, not the peer's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 08:44:09 -07:00
kyleandClaude Opus 5.5 d983fea024 fix(core): reassembled observations must decode canonically
The reassembler decoded with postcard::from_bytes, which accepts
trailing bytes. It now uses take_from_bytes and rejects a non-empty
remainder, as Frame::decode does (spec §10 F13).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 08:44:09 -07:00
kyleandClaude Opus 5.5 eda9746d8e fix(core): evict silent and contact-only peers (F8)
The idle check ran only in on_frame and only for the peer that just
sent, so a peer that went quiet, or never sent (contact-only), held its
slot forever: eight spoofed addresses locked out every newcomer
(review S1).

Peers now record when their contact started (added_at), and idle peers
(now - last_inbound.unwrap_or(added_at) > PEER_IDLE_MS) are swept as if
Lost before a peer is added and at the top of next_frame. A peer that
resumes after its idle window is an implicit new contact: its frame is
processed rather than silently dropped. A repeat Contact restarts the
idle clock.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 08:44:09 -07:00
kyleandClaude Opus 5.5 ac2bf37719 fix(core): scope acks, receipts and the F6 pin to the contact
- remove_peer purges only the removed peer's pending acks, not every
  peer's (review S9, F13).
- bcast_sent records for an address are dropped when its contact starts
  or ends, and a broadcast whose send fails has its record undone, so a
  broadcast Ack is accepted only for this contact's dispatched frames
  (review S10).
- A repeat Contact for a known peer starts a new contact: it resets the
  F6 pin and the per-contact bounce and receipt records (spec §10 F6).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 08:44:09 -07:00
kyleandClaude Opus 5.5 caa245dda4 fix(core): never trust store filtering for summaries and broadcast diff
Core re-checks dest when building the summary Bloom filter, rebuilding
a peer's broadcast send queue, and emitting a broadcast, so a store
whose for_each_broadcast_id yields addressed ids can't leak them into a
summary or forward them (expired) as broadcasts (review S11).

Adds UnfilteredStore, a test store whose iterators yield every held id,
released and broadcast ones included, pinning spec §4's 'core never
trusts a backend's filtering' for custody, acks and summaries (B8).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 08:44:09 -07:00
kyleandClaude Opus 5.5 5a7722bf25 docs(spec): decisions I–L from the 1.0 review
I: LogStore snapshots the log at the end of open and after the
set_wall_clock fold, so fold results and age lineage survive reboots.
J: hosts adopt the latest peer time estimate within a short window,
bounded, once per run.
K: local constants become pub(crate); only wire constants stay public.
L: quarantined entries are exempt from the count trims; the F9 summary
rate limit accepts at SUMMARY_INTERVAL_MS / 2; Observation.sig stays a
public field and §2's wording is corrected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 08:44:09 -07:00
kyleandClaude Opus 5.5 4f7fe8f2c2 docs(spec): decision H — WallClockStore and the Node wall-clock accessors
Record the public 1.0 API that 941a7ce introduced in place of the revived
store_mut backdoor: the opt-in WallClockStore trait (FoldReport,
set_wall_clock, peer_time_estimate) and Node::set_wall_clock /
Node::peer_time_estimate, bounded on S: WallClockStore. Updates §3's
public table and Node method list and §9's fold and time-from-peers
text; core still never interprets wall time.

Also split the drain_events sentence into its own §3 bullet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 08:44:09 -07:00
kyle 8425232ba1 release(core): tines-core 1.0.0 2026-10-22 08:44:09 -07:00
kyle 164cf97a5b test(core): fuzz harness and proptest twin 2026-10-22 08:44:09 -07:00
kyle ef539b494f refactor(core): WallClockStore trait replaces the store_mut backdoor
Task 10b reached LogStore::set_wall_clock through a general Node::store_mut.
Kyle: replace it with a narrower accessor. A WallClockStore trait (set_wall_clock
+ peer_time_estimate, implemented only by LogStore) now backs Node::set_wall_clock
and Node::peer_time_estimate, so no &mut Store escapes the facade.
2026-10-22 08:44:09 -07:00
kyle a43a4966e1 test(core): pinned vectors + MSRV gate 2026-10-22 08:44:09 -07:00
kyle 7c8d8b74e9 feat(hosts): trusted wall time from the system clock or a peer; set observed_at 2026-10-22 08:44:09 -07:00
kyle 9f028743b5 feat(store): quarantine held observations until a wall-clock downtime fold 2026-10-22 08:44:09 -07:00
kyle 5d05ad2252 docs(spec): amend F9 — drop already-sent skip to preserve lossy convergence 2026-10-22 08:44:09 -07:00
kyle c5378b1099 fix(core): peer pinning, idle eviction, replay and queue hardening 2026-10-22 08:44:09 -07:00
kyle 465410ca28 test(sim): pin bounce-table overflow behaviour 2026-10-22 08:44:09 -07:00
kyle e65c8287ba docs(agents): wire/local constant split and 1.0 trait extension rule 2026-10-22 08:44:09 -07:00
kyle 392c7c6b04 docs(core): split wire vs local constants 2026-10-22 08:44:09 -07:00
kyle 07907efec5 feat(core): settle the Store trait (flush, is_empty, conformance suite) 2026-10-22 08:44:09 -07:00
kyle 158689e265 refactor(core): narrow the 1.0 public API 2026-10-22 08:44:09 -07:00
kyle 94c3a3c8ad feat(core): reserved Signature slot on Observation 2026-10-22 08:44:09 -07:00
kyle e85b3704e0 feat(core): Dest type replaces to + lifetime_ms 2026-10-22 08:44:09 -07:00
kyle 193c7bddb8 feat(core): NodeEvent with counted drop reporting 2026-10-22 08:44:09 -07:00
kyle 99036514c2 feat(core): WIRE_VERSION byte on every frame 2026-10-22 08:44:09 -07:00
kyle 9d4ed7db24 fix(ci): install libdbus-1-dev for tines-bt's bluer
CI / ci (push) Successful in 1m10s
The workspace build pulls in bluer -> dbus -> libdbus-sys, which links
against the system D-Bus library. The ubuntu-latest runner has pkg-config
but not dbus-1.pc, so libdbus-sys's build script fails. Install the
libdbus-1-dev headers (matching Dockerfile.ros2) before running the gate.
2026-10-20 09:47:55 -07:00
kyleandClaude Opus 5.5 c3be9d0f83 docs(spec,plan): decision G — quarantine on open, time from peers
The store opens before trusted time. Observations already in the log
are quarantined (not summarised, offered, believed or age-trimmed;
addressed ones report released) until LogStore::set_wall_clock runs the
downtime fold. Trusted time comes from the robot's RTC/GPS, else a
peer's observed_at + age_ms (smallest age wins), else the robot's own
fail-safe. Supersedes "don't open the store until trusted time".

Plan: Task 10 rewritten (quarantine, set_wall_clock, FoldReport,
derived marker wall time); new Task 10b (host trusted-time state
machine, time from peers, observed_at).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 01:53:55 -07:00
kyle ce4e2ba9ca docs(plan): apply 1.0 review round 1 2026-10-20 01:39:10 -07:00
kyle 2d9d549815 docs(spec): amend decision G — plain wall time at open, derived marker wall time 2026-10-20 01:39:10 -07:00
kyle 9558917b70 docs(plan): tines-core 1.0 implementation plan 2026-10-20 01:29:07 -07:00
kyleandClaude Opus 5.5 aedb8026d5 docs(spec): 1.0 decisions F (flush) and G (age across downtime)
CI / ci (push) Failing after 33s
- F: Store::flush(now) with a no-op default; Node::flush(now) replaces
  Node::store_mut. The caller's now anchors ages (fixes LogStore's stale
  clock marker, F18).
- G: age_ms is documented as a lower bound. Robots are assumed to have a
  trusted wall clock (RTC or GPS); LogStore adds power-off time on open
  from a wall-clock flush marker. Unknown downtime expires held addressed
  messages and floors held broadcasts' age at unknown_downtime_age_ms.
  Hosts set observed_at.
- AGENTS.md: specs and plans change only to record decisions Kyle has
  discussed and agreed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 01:14:37 -07:00
kyleandClaude Opus 5.5 a4cde40e85 docs: sweep after the fleet fixes; approve the 1.0 spec
- README: custody chain and receipts replace epidemic forwarding; Ack
  frame; running on a robot (host crates, current limits); security;
  crate layout, constants and reading list.
- AGENTS.md: current spec list, receipt constraints and review focus
  7-8, first_seq rule, constants, host crates and the ros2 gate.
- THREAT_MODEL.md: re-checked at 89041ea. F1, F2, F5, F17 fixed; F3
  (single configured peer) and F4 partly fixed; new F18 (no scheduled
  flush, stale clock marker).
- CHANGELOG: Unreleased section for the fleet milestone and fixes.
- tines-bt README: single-peer limit; manual BLE check not yet recorded.
- tines service rustdoc: oversized-frame behaviour, JSON tagging, clock.
- 1.0 spec: approved (2026-10-20), including §10 and the adversarial
  fuzz mode.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 01:03:08 -07:00
kyle 89041ea89a docs(threat-model): mark F1–F5 and F17 resolved 2026-10-19 23:19:57 -07:00
kyle 7df9f41e9f fix(bt): framing, roles, NodeId binding, contact events 2026-10-19 23:18:35 -07:00
kyle d98895b9d6 fix(service): run the exchange on a poll loop 2026-10-19 19:22:43 -07:00
kyle 24b5a78f22 fix(store): fsync the parent directory on torn-tail truncation 2026-10-19 15:37:41 -07:00
kyle 4718b6f2eb fix(store): log format v1 with CRC and crash recovery 2026-10-19 15:35:42 -07:00
kyle 848721a70d fix(store): boot counter, durable releases, clock marker 2026-10-19 14:16:40 -07:00
kyle 6e2dc6f996 fix(hosts): one monotonic millisecond clock 2026-10-19 12:54:28 -07:00
kyle 2738de4959 fix(core): ack only observations the store kept
Exchange::accept enqueued a pending ack whenever
!(duplicate && addressed && released) held, even when the backend
store.insert returned Err (store full / backend error). On Err the
released flag computed to false, so an ack was still enqueued: a
receiver that did not keep the message acked it. For an addressed
offer that releases the sender's custody for a message nobody holds;
for a broadcast it fires a false confidence event at the sender.

Gate the ack enqueue on res.is_ok(): enqueue only when the store
actually retained the observation. The existing released-holder
exception still applies among kept copies. No bounce is recorded on
Err (already the case).

Adds a unit test exercising a capped store (StoreError::Full) for
both a broadcast and an addressed offer, and a tines-sim property
that drives custody with CappedStore-backed nodes and asserts the
"at least one holder" invariant still holds when stores fill.
2026-10-19 12:35:17 -07:00
kyle 836ab74e81 docs: fleet-fixes implementation plan 2026-10-19 12:02:33 -07:00
kyle 55cc7952bc docs: security assessment specs (fleet-fixes, host-hardening, 1.0 §10, threat model) 2026-10-19 12:02:33 -07:00
kyle 3b1a644b24 docs: tines-core 1.0 design spec (draft v4) 2026-10-18 23:47:54 -07:00
kyle 9281f9b5ae test(tines): give the unknown-belief ipc test its own store dir 2026-10-18 23:44:58 -07:00
kyle 22d003efb4 style(ros2): cargo fmt 2026-10-18 21:17:14 -07:00
kyle 69715a1505 fix(ros2): rclrs 0.8 API + bytes payload mapping
Rewrite tines-ros2/src/node.rs against the rclrs 0.8 API (Context::
default_from_env -> create_basic_executor -> create_node; type-inferred
subscriptions; thiserror-based RclrsError) and switch payloads from
std_msgs String to UInt8MultiArray:

* live topics publish the raw Observation.bytes as data (no UTF-8 lossy);
* inbound callbacks use msg.data verbatim;
* receipts are a 32-byte UInt8MultiArray (peer[..16] ++ id[..16]);
* supply the required MultiArrayLayout { dim: [], data_offset: 0 }.

Bump ros_env 0.1 -> 0.3 so the crate's std_msgs pins rclrs 0.8's rosidl
runtime 0.7 (was 0.6), and update Cargo.lock. Add #[allow(dead_code)] on
ManagedTopic::subscriber (drop-guard for the rcl subscription handle).

Keep the ROS2-free build green; no changes outside tines-ros2/ (and the
Cargo.lock entry).
2026-10-18 21:03:32 -07:00
kyle 049d4095b8 fix(store): crash-safe reopen, bounded record decode, durability docs 2026-10-18 19:16:47 -07:00
kyle 12f72aa6a2 ci: Dockerfile to verify tines-ros2 against ROS 2 Jazzy 2026-10-18 17:59:14 -07:00
kyle 4f61bbde00 fix(tines): harden IPC against malformed frames and lock poison
serve() now replies with Response::Error on a malformed JSON frame and
keeps the connection serving instead of dropping it on the parse error;
the per-connection loop only breaks on a clean disconnect
(UnexpectedEof). node.lock().unwrap() is replaced with poison-safe
into_inner() so one panicking handler can't take down the daemon.
Drop duplicate tines-core/tines-store/serde/serde_json from
dev-dependencies (only heapless is genuinely test-only).
2026-10-18 17:15:14 -07:00
kyle 20436badd0 feat(service): standalone tines service with Unix-socket IPC
New `tines` crate (added to workspace members) hosting a `Node<LogStore,
BtTransport>` and serving a tiny request/response IPC over a Unix-domain
socket for robots without ROS2. Four operations: `observe`, `belief`,
`events`, and `status`, over length-prefixed JSON frames.

The `serve` loop now serves each connection request-by-request (previously
it handled one request per connection, dropping the socket after the first
response). Integration tests drive `handle`/`serve` with a mock transport
that auto-acks observed broadcasts and echoes an empty Summary, so a
confidence event fires without a real BLE radio.
2026-10-18 16:41:02 -07:00
kyle 0926af9b28 fix(ros2): resolve borrow conflict in publish_one 2026-10-18 14:57:02 -07:00
kyle 7be09f8e9a Task 9: tines-ros2 — pure topic_name escaping (default, tested) + optional rclrs TinesNode (feature ros2)
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.
2026-10-18 14:46:57 -07:00
kyle d089d9edaa feat(bt): BLE transport over BlueZ with identity-preserving Addr
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.
2026-10-18 14:06:48 -07:00
kyle ef53204fca fix(store): rebase ages per-entry so non-idle flush doesn't under-count
LogStore::new folded `(now - last_clock)` into every entry's age_ms, but
age_ms is persisted at INSERT time and stored_at was never caught up at
flush. So an observation inserted before the last flush lost the
`last_flush - insert_time` after restart (a store that was non-idle
between insert and flush reported an age too small by that gap).

Fix by anchoring each entry's age to its own insert tick: carry
`stored_at` on the Obs record and fold `(now - stored_at)` per-entry at
load, instead of a single global fold against the last clock marker.
flush() now also advances each in-memory age to its current value so the
representation stays coherent within a run.

This is exact for idle stores and observations inserted before the last
flush. Adds tests/persistence.rs::ages_rebase_after_nonidle_flush
(insert A at tick 1000, flush at 3000, reopen at 9000 -> asserts true
age 8100, not 6100).
2026-10-18 13:14:33 -07:00
kyle 43b73f88cd feat(store): add tines-store persistent append-only LogStore
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.
2026-10-18 12:43:42 -07:00
kyle f9613b1c32 test(sim): custody chain and receipt properties 2026-10-18 12:14:51 -07:00
kyle 32fd86e31b docs: plan — Node::drain_events returns heapless::Vec 2026-10-18 12:00:31 -07:00
kyle d099f09b63 feat(core): Node::drain_events for confidence events 2026-10-18 11:56:38 -07:00
kyle fafda104ea feat(core): ack send path with custody/broadcast buffers
Buffer pending acks (acks for addressed, bcast_acks for broadcast,
ACK_BUFFER_CAP=256, overflow drops oldest), skip a released-duplicate
offer in accept(), and emit Ack frames in next_frame after a due summary
and before addressed/broadcast traffic, draining up to ACK_IDS_CAP ids
per destination and splitting across frames to fit mtu.

Release custody on an Ack only when the Ack confirms delivery to the
intended recipient: an Ack from a peer that merely holds an
epidemic-forwarded copy no longer releases custody, so the local copy
stays available to forward toward the recipient once the only contact to
it closes.
2026-10-18 11:21:49 -07:00
kyle 26a47c0d30 docs: plan — Exchange::drain_events is pub(crate) 2026-10-18 09:25:28 -07:00
kyle 6643b107ab feat(core): ack validation, custody release, confidence events 2026-10-18 09:13:35 -07:00
kyle 2a23cc390d feat(core): Ack wire frame (last Frame variant) 2026-10-18 07:39:07 -07:00
kyle 8703e723d5 docs: plan — 0.3.0 bump is the workspace version 2026-10-18 07:26:49 -07:00
kyle 0bb4d568ea feat(core): Store::release and is_released (0.3.0) 2026-10-18 07:22:45 -07:00
kyle 7d8ccbcf7d docs: fleet deployment v1 implementation plan 2026-10-18 07:15:45 -07:00
kyle e7fe26fc3c docs: fleet deployment v1 design spec (approved) 2026-10-18 07:12:20 -07:00
kyleandClaude Opus 5.5 eefc67e275 chore: ship LICENSE in each crate
Symlink the root Apache-2.0 LICENSE into tines-core and tines-sim so
`cargo package` includes the license text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 05:29:59 -07:00
kyle 3961a21a26 Licenses for crates.
CI / ci (push) Successful in 1m2s
2026-10-18 05:28:23 -07:00
kyleandClaude Opus 5.5 8b233c787f chore: release 0.2.1 with crates.io metadata
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>
2026-10-18 05:26:39 -07:00
kyleandClaude Opus 5.5 5c8bfe89ac chore: release 0.2.0; document the store contract and on_send_error
CI / ci (push) Canceled after 0s
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>
2026-10-18 05:24:30 -07:00
kyleandClaude Opus 5.5 203550bfc4 docs: expand README with diagrams; bring AGENTS.md up to date
CI / ci (push) Canceled after 0s
README: protocol, fragmentation, poll and addressed-delivery diagrams;
observation fields and identity; transport, belief and trim guidance;
simulator example; constants table.

AGENTS.md: drop the completed plan's task-by-task workflow and Task 14
notes, point at both specs, use scripts/ci.sh as the gate, and update
the identity-hash constraint to include `to`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 05:14:56 -07:00
kyleandClaude Opus 5.5 9a055b5b57 refactor(core): scan only addressed ids for custody; share frag plan
- Add Store::for_each_addressed_id (default: every id; MemStore filters)
  so custody selection no longer clones every broadcast observation on
  each poll.
- Factor fragment sizing into frag::plan and MAX_FRAGMENTS, used by both
  frag::fragment and the exchange, and by the reassembler's bounds.
- Document `to` in the Observation::id hash input, matching the
  addressed-delivery plan.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 05:09:07 -07:00
kyleandClaude Opus 5.5 cc39318673 fix(core): harden exchange send path from full-codebase review
- Cut every fragment of a stream from one encoding (age frozen at the
  first fragment) and bounds-check the slice: re-encoding mid-stream
  corrupted fragments and could panic when the tick went backwards.
- Fold the bloom summary to fit the MTU instead of sending a ~540-byte
  frame that small-MTU links reject; drop the sim's summary MTU
  exemption that hid this.
- Finish a peer's in-flight stream before starting another, and count
  an addressed message as delivered only after its last fragment.
- Alternate custody and broadcast per peer, rotate through addressed
  candidates, and evict the oldest bounce record when full, so neither
  path starves.
- Add Exchange::on_send_error; Node::poll uses it so Busy/Unreachable
  sends are retried rather than silently dropped.
- Clear bounce records per contact, record duplicates against their
  sender, and skip addressed messages that cannot fit the MTU.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 05:08:08 -07:00
kyleandClaude Opus 5.5 111a40236d ci: test the no-alloc build and harden the workflow
Run clippy, docs and tests for tines-core with --no-default-features,
gating the MemStore-based tests on `alloc` so they compile there. Fail on
rustdoc warnings, pass --locked everywhere, and pin the toolchain to
1.99.0. In the workflow, use read-only permissions, cancel superseded
runs, add a 20-minute timeout, and pin actions to commit SHAs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 05:07:12 -07:00
kyle 6907f000a2 ci: add GitHub Actions workflow
CI / ci (push) Successful in 2m10s
2026-10-18 04:30:00 -07:00
kyle d10e3cd88e test(core): close deferred coverage gaps 2026-10-18 04:28:38 -07:00
kyle 277daae89f fix(core): rebuild_send_queue excludes addressed messages 2026-10-18 03:57:08 -07:00
kyle 1d646344e6 style: cargo fmt --all 2026-10-18 03:28:51 -07:00
kyle c9ee3684f0 test(sim): addressed relay scenario and property tests 2026-10-18 03:22:35 -07:00
kyle db39dc1df0 feat(core): Node::observe_to and belief excludes addressed-to-others 2026-10-18 02:56:17 -07:00
kyle 8ad0a7415f feat(core): addressed store-and-forward in the exchange 2026-10-18 02:39:36 -07:00
kyle c59834737b feat(core): reject expired or malformed addressed messages 2026-10-18 02:03:52 -07:00
kyle 2c932e96c0 feat(core): summaries carry only broadcast observations 2026-10-18 01:55:29 -07:00
kyle 65c4aa72c0 feat(core): Store::for_each_broadcast_id 2026-10-18 01:49:01 -07:00
kyle 085b4d35da feat(core): Observation gains to/lifetime_ms; identity includes to 2026-10-18 01:41:24 -07:00
kyle 4be8042994 docs: add addressed-delivery implementation plan 2026-10-18 01:26:03 -07:00
kyle 720835de2f docs: pin custody count bound (lifetime + max_count) in addressed-delivery spec 2026-10-18 01:19:40 -07:00
kyle 0e737e3f4c chore: add cargo doc to ci gate 2026-10-18 01:17:16 -07:00
kyle 16198b4f10 chore: make crates publishable (version req, description, authors) 2026-10-18 01:16:36 -07:00
kyle 6c7b1ed492 docs: add addressed-delivery design spec (draft) 2026-10-18 01:14:57 -07:00
kyle 1a02ef2d93 docs: fix broken intra-doc links; make cargo doc clean 2026-10-18 01:12:19 -07:00
kyle 7ef81ced69 Cleaning up README diagram. 2026-10-18 00:23:59 -07:00
kyle 8a5afd17c8 Add tines.jpg diagram to README 2026-10-18 00:20:33 -07:00
kyle 946a3cec98 chore: license under Apache-2.0 (copyright K. Isom) 2026-10-18 00:01:48 -07:00
kyle 1257f227ce refactor(sim): move FrameBuf on dup; vary per-node obs; test disconnect/unreachable 2026-10-17 23:58:15 -07:00
kyle 1752bd23ff refactor(core): dedupe trim/belief comparators and emit_one; add coverage tests 2026-10-17 23:58:15 -07:00
kyle 80ccd25dde fix(core): reset reassembler slot on buffer overflow; drop tautological assert 2026-10-17 23:58:15 -07:00
kyle 09a676c243 docs: fix Bloom |1 placement and doubled 'The'; PairTransport 'broadcast' wording 2026-10-17 23:58:15 -07:00
kyle ab9b892d94 chore: ci script and README 2026-10-17 21:48:37 -07:00
kyle 4706004f47 test(sim): property tests for convergence, order independence, monotonicity 2026-10-17 21:42:16 -07:00
kyle 9de7157ee2 feat(sim): harness and convergence scenarios 2026-10-17 17:32:12 -07:00