- 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>
6.9 KiB
AGENTS.md — tines
Instructions for AI agents working on tines: opportunistic shared memory for mostly-offline robots. Read this before touching code.
What tines is
Small robots collect facts about the world while mostly offline. When two of them happen to meet in range, they swap what they know so each one's picture of the world improves. No robot depends on receiving anything, but there is no reason not to share.
tines is the library that does this: a store of timestamped-by-age
observations, a belief layer derived from them, and a delay-tolerant exchange
protocol over any datagram link. Read the full intent, goals, anti-goals, and
architecture in the spec first:
- Core design (authoritative):
docs/superpowers/specs/2026-10-16-tines-design.md - Addressed delivery (authoritative):
docs/superpowers/specs/2026-10-18-tines-addressed-delivery.md - Fleet deployment v1 (authoritative):
docs/superpowers/specs/2026-10-18-tines-fleet-deployment-design.md— supersedes the addressed-delivery spec's epidemic forwarding (custody chain with handoff receipts). - Fleet fixes (done):
docs/superpowers/specs/2026-10-19-tines-fleet-fixes.md— security-assessment findings F1–F5 and F17. - tines-core 1.0 (next, core):
docs/superpowers/specs/2026-10-18-tines-1-0-design.md— wire version, public API, signature slot, core hardening (§10). - Host hardening (next, hosts):
docs/superpowers/specs/2026-10-19-tines-host-hardening.md— findings F10–F12. - Implementation plans:
docs/superpowers/plans/
The plans argue from the specs. Where a plan is silent, the spec rules.
Do not edit the specs or the plans on your own initiative — they are design
history, not source. Change one only to record something Kyle has discussed
and agreed, and record it as a dated decision in that document. Anything
else that needs changing in a spec goes to Kyle first.
README.md is the user-facing overview and should track the code.
THREAT_MODEL.md is the living security assessment: when a change fixes or
changes a finding, update its status there (§1 and §10) with the commit.
CHANGELOG.md records each release.
Architecture (three layers)
memory — "my priors": beliefs per topic, derived from storage under a policy
storage — append-only log of raw observations; local trimming policy
comms — contact-driven exchange of observations; transport trait + adapters
Only observations cross the wire. Memory is a pure function of storage plus a
policy and is never transmitted. See the spec's "Architecture" section for the
Observation, Store, BeliefPolicy, Transport, and exchange protocol
details.
Workflow
Changes are TDD: write the failing test → see it fail → implement → see it pass → commit.
- Keep the public API small. New public names in
tines-core(types, trait methods, functions) need a reason; call them out when you add them. - Before committing, run the full gate, which is what CI runs:
sh scripts/ci.sh(fmt, clippy and rustdoc with warnings as errors, tests with and withoutalloc, and thethumbv7em-none-eabihfbuild). - Use conventional commit messages (
fix(core): …,ci: …,docs: …).
The Node facade is core, not application glue: an application on any target
should only need to construct a Node with its NodeId, store, and transport,
call poll() in its loop, and observe() when it sees something.
Hard constraints (non-negotiable in tines-core)
tines-coreis#![no_std];allocis a cargo feature (default on). With--no-default-featuresit must build forthumbv7em-none-eabihf. OnlyMemStoremay requirealloc.- No async, no threads, no I/O, and no panic on untrusted input in core. Every frame from the wire is untrusted: malformed bytes are dropped, never unwrapped.
- Core never maps a transport
Addrto aNodeId; every frame carries the sender'sNodeId. - Identity hash covers
(observer, seq, topic, payload, to)only.age_ms,observed_atandlifetime_msare excluded. - Addressed observations (
to = Some) never appear in a summary Bloom filter, and an addressed observation past itslifetime_msis never stored or forwarded. first_seqpassed toNode::newmust not repeat across reboots (boot_count << 32, with a persisted boot counter;LogStore::first_seqprovides it). A repeated seq after reboot collides in the ID hash and must dedupe silently (insertreturnsOk(false)).- Receipts: a receive the store rejects (
Err, e.g.Full) is never acked, addressed or broadcast; an addressed duplicate whose local copy is released is never acked; anAckis accepted only from a peer the id was sent to this contact. NodeId = [u8; 16],ObservationId = [u8; 16],Tick = u64(local monotonic ms, supplied on every call).
Review Focus (each covered by a test; keep it that way)
- Reused
seqafter reboot → store dedupes,insertreturnsOk(false). - Oversized
topic/payloadatobserve()→Err(TooLarge), never truncate/panic. - Garbage frames → dropped, no panic, no store change.
- Frame from an address with no prior
Contact→ treated as implicit contact. - Tick going backwards → age saturates at stored value, never underflow.
- Every emitted frame fits the transport's
mtu(), including summaries. - Store-full receive → no
Ack; custody stays with the sender (unit test and the full-store sim property). - Released addressed duplicate → no
Ack.
Compile-time constants (tines-core/src/consts.rs)
MAX_TOPIC=64, MAX_PAYLOAD=1024, MAX_PEERS=8, SUMMARY_BYTES=512,
BLOOM_K=3, SUMMARY_INTERVAL_MS=2000, REASSEMBLY_TIMEOUT_MS=5000,
SEND_QUEUE_CAP=256, MAX_FRAME=1500, ACK_IDS_CAP=90,
EVENT_QUEUE_CAP=64, BCAST_SENT_CAP=256, ACK_BUFFER_CAP=256. Match the
exact values above.
Crate layout
tines/
tines-core/ no_std core: Observation, Store/Transport/BeliefPolicy traits,
exchange state machine, Node facade, in-memory store, reference
policies
tines-sim/ std: simulator, contact traces, convergence, relay and custody
property tests
tines-store/ std: LogStore, the persistent append-only backend
tines-bt/ std: BLE transport over BlueZ (bluer)
tines/ std: standalone service, Unix-socket JSON IPC
tines-ros2/ std: ROS2 node; rclrs behind the `ros2` feature
Only tines-core is headed for 1.0; the host crates stay 0.x (1.0 spec, D4).
They're std-only and may use threads and I/O, but they're still the trust
boundary for radio, IPC and disk input (see THREAT_MODEL.md).
tines-ros2 with --features ros2 is outside the default gate: check it
with the ROS2 Docker job (Dockerfile.ros2, scripts/check-ros2.sh).
Not started
tines-udp, tines-sqlite, tines-cli, and tines-serial (from the core
spec's crate layout) belong to a future plan. Don't start them unless asked.