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>
106 lines
4.7 KiB
Markdown
106 lines
4.7 KiB
Markdown
# 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`
|
|
- **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 — they are design history, not source.
|
|
`README.md` is the user-facing overview and should track the code.
|
|
|
|
## 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.
|
|
|
|
1. 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.
|
|
2. 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 without `alloc`, and the `thumbv7em-none-eabihf` build).
|
|
3. 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-core` is `#![no_std]`; `alloc` is a cargo feature (default on). With
|
|
`--no-default-features` it must build for `thumbv7em-none-eabihf`. Only
|
|
`MemStore` may require `alloc`.
|
|
- 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 `Addr` to a `NodeId`; every frame carries the
|
|
sender's `NodeId`.
|
|
- Identity hash covers `(observer, seq, topic, payload, to)` **only**.
|
|
`age_ms`, `observed_at` and `lifetime_ms` are excluded.
|
|
- Addressed observations (`to = Some`) never appear in a summary Bloom
|
|
filter, and an addressed observation past its `lifetime_ms` is never
|
|
stored or forwarded.
|
|
- `Node::next_seq()` must be persisted by the application across reboots
|
|
(`boot_count << 32 | counter`); a repeated seq after reboot collides in the
|
|
ID hash and must dedupe silently (`insert` returns `Ok(false)`).
|
|
- `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)
|
|
|
|
1. Reused `seq` after reboot → store dedupes, `insert` returns `Ok(false)`.
|
|
2. Oversized `topic`/`payload` at `observe()` → `Err(TooLarge)`, never truncate/panic.
|
|
3. Garbage frames → dropped, no panic, no store change.
|
|
4. Frame from an address with no prior `Contact` → treated as implicit contact.
|
|
5. Tick going backwards → age saturates at stored value, never underflow.
|
|
6. Every emitted frame fits the transport's `mtu()`, including summaries.
|
|
|
|
### 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`. 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 and relay
|
|
property tests
|
|
```
|
|
|
|
## 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.
|