Files
alkstore/tasks/mem-engine-foundations.md
T
glm-5.3-flash 6632dcce1f mem-engine-foundations — the mem engine's foundations land (alkstore-mem, wave 6 task 1 of the ADR-024 chain): fourth workspace member with the manifest surface ADR-024 §1 pins — alkstore versioned path pin (ADR-017 §4.1) plus tokio restricted to the wasm-supported subset (sync + time; dev-deps rt/macros for the current-thread-flavor tests), no driver deps, no parking_lot (the description's prose named it, but the ADR + acceptance criterion pin the manifest tighter — short-held internal guards are std::sync::Mutex behind a state::Guard trait that folds poisoning away structurally, guards never held across awaits or user code, tokio mpsc the cross-await primitive). Five modules re-exported from lib.rs whose crate docs carry the ADR-016 posture statement (honest-ephemeral, full contract v1, wasm32-compile-clean identity, architecture sketch with the wave-6 construction line). MemStore::new() is the engine-native isolated constructor (ADR-024 §4's documented open-prose exception): fresh guarded state core (state.rs — empty named per-mechanism sections QueueState/StreamState/LockState/ScheduleState, shapes owned by the mechanism tasks), fresh wake bus, fresh clock per open; teardown is drop driving WakeBus::close() — no explicit close form. Wake substrate (wakes.rs): WakeBus::attach → WakeFeed (one unbounded mpsc feed per subscriber, detach-on-drop, attach under the registry guard so attach-after-commit no-replay is structural), fire_commit_wakes(channels) as the commit-ordered wake source's entry point (guard makes watermark bump + per-channel fanout one operation — per-subscriber FIFO by commit order, never coalesced), close empties senders so engine drop disconnects every feed; commit_watermark is a cfg(test) ordering observable only. Clock (clock.rs): one accessor Clock::unix_now over SystemTime, freeze/advance/unfreeze as the cfg(test) deterministic-seam; MemStore clock/wakes cfg(test) accessors. Validation (validation.rs) carries no new rule — pin module only (empty/whitespace InvalidName, prefix ReservedName, RESERVED_LISTENER_RECONNECTED rejected though unused here); mechanisms route core's validate_shared_name/local_name directly. Targeted allow(dead_code) on not-yet-wired scaffold surfaces so clippy -D warnings stays green pre-consumer (mechanism tasks own exhausting the allows); tokio time pinned unexercised (lock-TTL/scheduler consumers arrive with their tasks). Unit pins: 17 mem tests — two-instances-share-nothing (Arc pointer identity + behavioral: frozen clock isolation, cross-instance wake silence), state section independence/relock, wake routing (channel-scoped + foreign silence, per-notify never coalesced, watermark-ordered FIFO, no-replay, close disconnects all + post-close inert, dropped feed detaches), engine-drop closes feeds, clock determinism, validation pin row. Task file: status completed, Notes (decisions of record: parking_lot rejection, empty sections, substrate API shape, no-close, scaffold allow posture, unexercised time feature) + Summary filled. Verified: cargo build; workspace cargo test green (pg 124/25/9, sqlite 191/25, core 25, suite 3, mem 17); clippy --all-targets -D warnings clean; cargo fmt --check clean; cargo check --target wasm32-unknown-unknown -p alkstore-mem clean — the wave-6 wasm gate starts green from this task onward
2026-10-10 11:27:52 +00:00

9.1 KiB

id, name, status, depends_on, scope, risk, impact, level, tags
id name status depends_on scope risk impact level tags
mem-engine-foundations Mem engine — crate scaffold, `MemStore::new()`, guarded state core, wake routing completed
moderate medium component implementation
wave-6
mem-engine

Description

The mem engine's foundations (wave 6 — ADR-024): a new workspace member alkstore-mem and the state core every later task builds on.

  • Crate + manifest: workspace member per ADR-024 §1 — dependencies are alkstore (versioned path pin per ADR-017 §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 §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

  • alkstore-mem is a workspace member compiling clean; manifest dependency surface is core + tokio (sync + time) only
  • MemStore::new() constructs; two instances share nothing (pinned by a unit test)
  • The guarded state core, validation rules, wake-routing substrate, and clock accessor exist with unit pins
  • cargo check --target wasm32-unknown-unknown -p alkstore-mem clean from this task onward (tokio subset discipline enforced here, not retro-fitted)
  • 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.