--- id: mem-engine-foundations name: Mem engine — crate scaffold, `MemStore::new()`, guarded state core, wake routing status: completed depends_on: [] scope: moderate risk: medium impact: component level: implementation tags: [wave-6, mem-engine] --- ## Description The mem engine's foundations (wave 6 — [ADR-024](../architecture/decisions/024-mem-engine.md)): a new workspace member `alkstore-mem` and the state core every later task builds on. - **Crate + manifest**: workspace member per [ADR-024](../architecture/decisions/024-mem-engine.md) §1 — dependencies are `alkstore` (versioned path pin per [ADR-017](../architecture/decisions/017-contract-versioning.md) §4.1, matching the other engines' manifests) plus tokio restricted to the **wasm-supported subset (sync + time only)**. No driver dependencies. `thiserror` via core; no other crates.io surface. - **Constructor**: `MemStore::new()` — engine-native, no connection string, no file path (the one documented exception to the core-contract open prose, amended by [ADR-024](../architecture/decisions/024-mem-engine.md) §4). Each `new()` yields a fully isolated instance sharing nothing (the suite factory's isolation contract); teardown is drop. No `PgOpts`-equivalent opts struct exists — nothing to configure (if a knob surfaces during the wave, `MemOpts` with `..Default::default()` follows the family opts pattern; not assumed now). - **Guarded per-mechanism state core**: the store instance is the state — per-mechanism structures (queues+jobs, dead letters, streams+offsets, locks, schedules) behind the family's locking discipline (parking_lot short-held internal locks per AGENTS convention 3; tokio primitives for cross-await coordination). Layout is module-per-file under `src/`, mirroring the other engines. Subsequent mechanism tasks own their tables' shapes; this task owns the shared scaffolding: the guarded-state container idiom, name validation at entry (same `InvalidName`/`ReservedName` rules as every other engine — reserved prefix `__alkstore_`, the one reserved string `__alkstore_listener_reconnected__` unused here but still rejected), and the wake-routing substrate (in-process channels; a mechanism registers its commit-ordered wake source, receivers attach per channel). - **Wake substrate shape**: no batching boundary exists — wakes deliver at the commit boundary directly to each open receiver, per-subscriber FIFO by commit order (engine-mem.md §Architecture; coalescing not applicable). The routing core lives here; the `listen()`/`WakeReceiver` surface lives in `mem-engine-notify-listen`. - **Clock posture**: every timestamp (unix-seconds stamps, visibility deadlines, TTLs, scheduler boundaries) reads the process clock — there is no server clock to defer to. Keep the clock behind one internal accessor so tests can observe timestamps deterministically the same way the SQL engines' row shapes do. The `Store` trait impl itself is ADR-007-shaped and lands in `mem-engine-seam-tx` (the tx overlay is the load-bearing half); this task lands everything up to the trait boundary. ## Acceptance Criteria - [x] `alkstore-mem` is a workspace member compiling clean; manifest dependency surface is core + tokio (sync + time) only - [x] `MemStore::new()` constructs; two instances share nothing (pinned by a unit test) - [x] The guarded state core, validation rules, wake-routing substrate, and clock accessor exist with unit pins - [x] `cargo check --target wasm32-unknown-unknown -p alkstore-mem` clean from this task onward (tokio subset discipline enforced here, not retro-fitted) - [x] Workspace gates green (`cargo build`, `cargo test`, clippy `-D warnings`, fmt) ## References - docs/architecture/decisions/024-mem-engine.md §1/§4 - docs/architecture/engine-mem.md §Identity and posture, §Architecture - docs/architecture/core-contract.md §Naming / reserved namespace - tasks/pg-engine-schema.md (the wave-4 rhythm this wave mirrors — its "foundations first" slot) ## Notes > Filled by implementation agent, 2026-10-10. 1. **parking_lot rejected — the ADR won the manifest.** The description's prose named "parking_lot short-held internal locks per AGENTS convention 3", but ADR-024 §1 and the acceptance criterion pin the manifest to core + tokio (sync + time) only. The guarded-state container uses plain `std::sync::Mutex` (wasm-fine, no crates.io surface) behind a `Guard` trait whose `guard()` folds poisoning into `into_inner` — structurally unreachable anyway, since no guard is ever held across an `.await` or user-provided code; tokio primitives (`mpsc` unbounded) are the cross-await coordination. Downstream mechanism tasks follow this idiom (`state::Guard`). 2. **Guarded-state shapes deliberately empty** — `state.rs` carries the container plus named empty per-mechanism sections (`QueueState` = jobs + dead letters, `StreamState` = log + offsets, `LockState` = TTL registry, `ScheduleState` = schedule table); each mechanism task owns its section's actual shape. 3. **Wake substrate API shape** (`wakes.rs`): `WakeBus::attach(channel) -> WakeFeed` (one unbounded mpsc feed per subscriber; attach lands under the registry guard, so attach-after-commit no-replay is structural), `fire_commit_wakes(channels)` is the commit-ordered wake source's entry point (mechanisms fire at the commit boundary; the guard makes bump + per-channel fanout one operation — per-subscriber FIFO by commit order, never coalesced), `close()` is the engine-drop path (MemStore's `Drop` calls it; senders live only in the bus, so a drop disconnects every feed regardless of who holds one), and `commit_watermark()` is a `cfg(test)` ordering observable only — never contract surface. `WakeFeed::rx()` exposes the raw mpsc receiver; the consumer-visible `WakeReceiver` bridge (recv/try_recv/recv_timeout forms) is `mem-engine-notify-listen`'s surface over this primitive. 4. **Clock** (`clock.rs`): one accessor `Clock::unix_now()` reads `std::time::SystemTime` (process clock only, per engine-mem.md); `freeze`/`advance`/`unfreeze` are the `cfg(test)` deterministic observation seam. `MemStore::clock()` `cfg(test)` accessor. 5. **Validation carries no new rule** — `validation.rs` is the pin module only (empty/whitespace-only → `InvalidName`, reserved prefix → `ReservedName`, `RESERVED_LISTENER_RECONNECTED` rejected even though unused here); every future mem entry point routes through core's `validate_shared_name`/`validate_local_name` directly, same as the SQL engines. 6. **Scaffold dead-code posture**: targeted `#[allow(dead_code)]` on the not-yet-wired scaffold surfaces (the `wakes` module, the empty `State` sections, `MemStore`'s `state`/`clock` fields, `Clock::unix_now`) so clippy `-D warnings` stays green with the gate before consumers exist — the mechanism tasks own using (and thereby exhausting) these allows. 7. **No `MemStore::close()`** — ADR-024 §4 says teardown is drop; there is no explicit close form (the sqlite/pg engines' `close` is connection machinery). If `mem-engine-integration` wants one for symmetry, that is the place to decide. 8. **tokio `time` feature is pinned but currently unexercised** — no mem code reads tokio time yet (lock TTLs and scheduler boundaries are their tasks' consumers); the manifest carries it per this task's description so the wasm-subset discipline is enforced at birth rather than widened later. ## Summary > Filled on completion, 2026-10-10. Landed `alkstore-mem` as the fourth workspace member — crate scaffold + manifest (deps: `alkstore` path pin per ADR-017 §4.1 + tokio `sync`/`time` only; dev-deps tokio `rt`/`macros`), five modules (`store`, `state`, `wakes`, `clock`, `validation`), re-exported from `lib.rs` whose crate docs carry the ADR-016 posture statement (honest-ephemeral, full contract v1, wasm32-compile-clean identity). `MemStore::new()` implements the engine-native isolated-constructor contract (ADR-024 §4): new guarded state core, wake bus, and clock per open, teardown = drop driving `WakeBus::close()`. Unit pins: 16 tests — two-instances-share-nothing (Arc pointer identity + behavioral: frozen clock on one instance leaves the other's process time intact; a fire through one instance's bus never reaches the other's feed), guarded-state section independence/relock, wake routing (channel-scoped per-subscriber delivery + foreign-channel silence, per-commit-notify never coalesced, watermark-ordered FIFO, attach-after-commit no-replay, close disconnects every live feed + inert post-close fire, dropped feed detaches), engine-drop closes open feeds, clock determinism (freeze/advance/unfreeze), validation pins (4). Verified: `cargo build`, workspace `cargo test` green (pg 124 lib / 25 suite / 9 schema, sqlite 191 lib / 25 suite, core 25, suite harness 3, **mem 17**), clippy `--all-targets -D warnings` clean, `cargo fmt --check` clean, and `cargo check --target wasm32-unknown-unknown -p alkstore-mem` clean — the wave-6 wasm gate starts green from this task onward.