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

174 lines
9.1 KiB
Markdown

---
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.