diff --git a/CLAUDE.md b/CLAUDE.md index 1431c9d..ecfa772 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -58,7 +58,7 @@ The big picture spans several crates/layers; understand these before touching co - **Generic core** — `Actor`, mailbox, supervision. Kept **transport- and domain-agnostic** so the nexus adapter and lite-bombay sit on top. The kameo `remote` (libp2p) feature is replaced by a `zenoh` feature; `macros`/`tracing`/`console`/`otel`/`metrics` features are repurposed. Crate/feature layout is design card **#66** and gates the rewrite shape. - **Zenoh remote layer** — actors are addressable in the dataspace by **key-expr** (the one invasive change escaping `src/remote/` is `ActorId`: libp2p `PeerId` → Zenoh key-expr, #2). `ask` = Session get / query-reply; `tell` = put; **death-watch** = one liveliness token per actor (subscriber gets a `Delete` on drop). DHT registry → key-expr discovery (#3). -- **nexus adapter** — nexus ships *primitives, never a runner*. The loop, dispatch, cursor, lifecycle, supervision, command bus, projection/saga runners are **Bombay's** to build. This boundary is dense and easy to get subtly wrong — **read issue #59 ("nexus integration contract") before implementing anything in the adapter.** Key invariants: single-writer-per-aggregate (optimistic-concurrency conflicts are surfaced, never retried internally); `GlobalSeq` monotonic but not gapless; `Version` is 1-based `NonZeroU64`; the subscription cursor never returns `None` (caught-up = wait, forever-driver); exactly-once is the runner's to assemble from at-least-once + atomic commit; the kernel is `no_std`/`no_alloc`/WASM-capable. +- **nexus aggregate-runner** (in-crate, feature-gated — *not* a separate adapter; see #66) — nexus ships *primitives, never a runner*. The loop, dispatch, cursor, lifecycle, supervision, command bus, projection/saga runners are **Bombay's** to build, as an in-crate module behind the `aggregate` feature (`nexus` + `nexus-store` are feature-gated deps). This boundary is dense and easy to get subtly wrong — **read the nexus integration contract (private `bombay-nexus#4`) before implementing anything in the aggregate-runner.** Key invariants: single-writer-per-aggregate (optimistic-concurrency conflicts are surfaced, never retried internally); `GlobalSeq` monotonic but not gapless; `Version` is 1-based `NonZeroU64`; the subscription cursor never returns `None` (caught-up = wait, forever-driver); exactly-once is the runner's to assemble from at-least-once + atomic commit; the kernel is `no_std`/`no_alloc`/WASM-capable. - **lite-bombay** — thin but self-sovereign mobile client; hosts its own KERI identity, discovers a nearby router in client mode. ## Build & tooling (per the foundation cards) @@ -77,7 +77,7 @@ Bombay is the runtime/adapter for nexus and holds the **same hygiene bar** (the **0. Facts only — no assumptions, no opinions.** If you don't know, say so and research it; don't fill gaps with plausible-sounding guesses about APIs, crate behavior, or performance. No "I think / cleaner / feels better." Technical claims about algorithms, concurrency, or crypto must cite a primary source (papers, the actual repo, RFCs/specs) — "common knowledge" is not a source. Uncertainty is a fact: state it rather than collapsing it into confidence. -**1. Atomicity.** Any operation doing 2+ store calls (multi-key reads, read-then-write) must share one transaction/snapshot — never two independent reads. Derived state (projections, snapshots) is best-effort and re-derivable; it must never block event persistence. (Mirrors nexus contract #5/#13 in issue #59.) +**1. Atomicity.** Any operation doing 2+ store calls (multi-key reads, read-then-write) must share one transaction/snapshot — never two independent reads. Derived state (projections, snapshots) is best-effort and re-derivable; it must never block event persistence. (Mirrors nexus contract #5/#13 in `bombay-nexus#4`.) **2. Arithmetic safety.** No bare arithmetic in production — use `checked_add`/`checked_sub`, return `Err` on overflow. `saturating_add` is banned (silently stops progress). No `try_from(...).unwrap_or(MAX)`. `debug_assert` is NOT a safety check (compiled out in release) — use a runtime check for anything that would corrupt data.