--- id: mem-engine-streams name: Mem engine — streams (in-memory log, per-consumer offsets, trim) status: pending depends_on: [mem-engine-seam-tx, mem-engine-notify-listen] scope: moderate risk: medium impact: component level: implementation tags: [wave-6, mem-engine, streams] --- ## Description The streams mechanism over an in-memory event log + per-consumer offset table (engine-mem.md §Mapping the contract, streams row): - **`StreamHandle` surface** ([ADR-019](../architecture/decisions/019-mechanism-handle-surfaces.md) §2): `publish` / `publish_with_key` (present key must be non-empty → `InvalidName`), `read_since` / `read_from_consumer` (cursor reads, `offset ASC`, extents clamp empty per [ADR-023](../architecture/decisions/023-fourth-review-round.md) §2), `save_offset` / `get_offset` (monotone saves — below-checkpoint saves are silent no-ops, `Err` arm carries `Database` only), `trim_to` (delete `offset <= horizon`, exact-boundary, returns deleted count, negative horizon deletes nothing idempotently, **wakes nothing**, surviving rows keep offsets, never renumbered), and `subscribe` (durable consumption: attach, read to tail, resume from saved offset; wake-driven with re-read — the wake substrate delivers at commit; explicit saves only, no auto-checkpoint). - **Ordering**: engine-assigned monotonically increasing per-stream offsets, global FIFO by offset — the ADR-015 ordering row, mem's own counter (per-stream; absolute values need not equal the SQL engines' — the equivalence rows pin relative order). - **`StreamEvent` shape** exactly as pinned ([ADR-015](../architecture/decisions/015-streams-depth.md) §3): `{ offset, stream, key, payload, created_at }` — `created_at` is unix-seconds at publish, informational, from the clock accessor. Reads from a trimmed-away region resume at the horizon's first remaining row. - **Keyed publish rides the tx overlay** (staged `publish_with_key_tx` landed in `mem-engine-seam-tx`); this task's non-tx forms, receiver, and trim live here. - **Receiver close arms** ([ADR-021](../architecture/decisions/021-tx-reads-and-value-shape-fixes.md) §5): `recv()`'s `Err` carries `Database` only (payloads cross raw; `Codec` at `payload_as`); close is terminal — the one close path is engine drop (the notify-listen task's close arm, realized identically); no reconnection concept (the pg stays-open-across- reconnect row is absent from mem's column). ## Acceptance Criteria - [ ] Full `StreamHandle` surface over the in-memory log + per-consumer offsets - [ ] The ADR-015 properties unit-pinned: global FIFO, immutable never-renumbered offsets (gaps legal post-trim), exact-boundary trim semantics (reads resume at horizon; saved sub-horizon offsets stay valid; trim wakes nothing), monotone save composition (direct + receiver forms interleave) - [ ] Key round-trips exactly (`None` vs `Some`); empty-`Some`-key rejected at every entry point - [ ] Extent/offset numeric-domain guards at entry — pinned - [ ] `cargo check --target wasm32-unknown-unknown -p alkstore-mem` clean; workspace gates green ## References - docs/architecture/core-contract.md §streams - docs/architecture/decisions/015-streams-depth.md; 019-mechanism-handle-surfaces.md §2/§6; 021 §5; 023 §2 - docs/architecture/engine-mem.md §Mapping the contract (streams row) - tasks/pg-engine-streams.md (the structural twin's task of record) ## Notes > To be filled by implementation agent ## Summary > To be filled on completion