Files
alkstore/docs/architecture/decisions/001-crate-split.md
T
glm-5.3-flash 060f262e62 ADR-024 — the mem engine adopted as the family's third engine (wave 6; release readiness renumbered to wave 7)
Row-first gate: consumer-inventory.md gains an engine-tier section — the mem-engine need recorded operator-authority, dated 2026-10-10 (wasm32 sandboxing across the alk protocol family + downstream ffi/napi/python-adapter economics; rusqlite/tokio-postgres structurally out on wasm, so mem is the only triad member the sandbox can carry).

ADR-024 (decisions/024-mem-engine.md) pins the decision set: (1) separate `alkstore-mem` crate — discharges and amends ADR-001 §4 (superseded in part: full contract-v1 engine, not an "implementation convenience"; annotation on ADR-001 §4 + Status, class-1 window still open); (2) honest-ephemeral posture — no durability, single-process, ephemeral by design, never fleet-valid (ADR-010 §3's shared-pool predicate fails structurally), riding ADR-016's carriers unchanged (identity + docs + matrix); (3) full contract v1 tier with the asymmetry classification — `PayloadTooLarge` never produced (SQLite arm), no reconnection concept (reconnect-wake/watcher rows absent from mem's per-engine column), receiver close at engine drop, wake coalescing N/A; (4) `MemStore::new()` engine-native constructor — the one documented exception to the open-prose, core-contract.md Store concept amended; shared-instance (two opens → one engine) rejected with the ADR-016 §4-style re-entry gate (consumer-inventory row), register stays closed; (5) wasm32 — compile-clean for wasm32-unknown-unknown plus the suite's mem column green on host under a current-thread runtime flavor are wave-6 acceptance items; on-target suite execution scoped out with a named collapse condition (wasm-bindgen-test adapter crate) — a scope decision, not a pending-client hedge; (6) mem is not a core dev-dependency — core's doc examples stay mock-based, ADR-001 §4's doctest posture declines.

engine-mem.md (draft): the in-process mapping — guarded per-mechanism state (no pool, no schema bootstrap, no forwarder, no spawn_blocking seam), the tx overlay (staged *_tx effects merged atomically at commit; read-your-own-writes easy, drop-=-rollback trivial; wake-at-commit structural — the seam pg needed pg-fix-tx-wake for, mem pins from birth via the existing tx-commit-atomicity rows), ADR-012 §2 third owner of the curve/opts/@every arithmetic (three-way equivalence, ADR-022's StoreFactory reused verbatim — born pinned).

deployment.md: host-semantics row (single-process, ephemeral, never fleet-valid, wasm32-clean), mem connection-budget subsection (none — nothing to budget), durability-knobs row (the honest row is the absence), toolchain row (wasm32 subset + current-thread-clean, acceptance-cited). README/overview: engine-mem.md row, ADR-024 row, family table entry (alkstore-mem, no driver deps), current-state updated to the implementation phase with engine specs' stable statuses reflected.

implementation.md: wave table gains wave 6 (mem engine, depends waves 1 + 5) and renumbers release readiness to wave 7 (fuzzing decision — the one remaining decided-at-implementation deferral — rides wave 7); wave-6 section records the born-pinned posture, acceptance items, the one suite-harness pre-task (past_stamp_sleep blocking sleep → async sleep, review 003's properties.rs:1683 note), and the window riders (review 003 Finding 1: the pg transient-fault seam, MEDIUM, rides wave 6; Findings 2–3 follow their park into wave 7's pre-release); wave-6 review gate defined; Decided points updated (mem discharged, fuzzing renumbered); stale wave-6 references swept across review docs and the landed tasks' notes (release-readiness items renumbered or annotated).

Docs-only change; verified by cross-reference audit (all ADR/OQ/anchor references resolve, residual wave-6 mentions are correct under the new numbering or carry dated annotations).
2026-10-10 09:42:40 +00:00

5.6 KiB

ADR-001: Reactive-core crate + per-engine crates

Status

Accepted (capability-flags content annotated 2026-10-06 by ADR-016; §4 superseded in part 2026-10-10 by ADR-024 — the mem engine exists as the separate alkstore-mem crate, a full contract-v1 implementation, not an implementation convenience)

Context

The store must present one reactive interface (notify, streams, queues, locks, scheduler, outbox) over two backing engines, SQLite and Postgres. The engines differ sharply in how much of that surface is natively theirs:

  • The SQLite engine rides honker's existing, battle-tested machinery (published honker-core — a port-and-adapt of a sync library to the family's async-facing-trait posture). (Ownership since resolved: the substrate is forked into owned code — ADR-011.)
  • The Postgres engine is the build-heavy side: LISTEN/NOTIFY wiring and the pg-boss-family queue schema are built by this crate.

A single crate with feature-gated engines would force both engines' dependency graphs into every downstream binary that enables both features. That is not hypothetical: rusqlite 0.40's libsqlite3-sys link collision with sqlx's sqlite driver is a real constraint (see ADR-003); single-driver binaries are the only clean escape.

Scope evidence lives in docs/research/consumer-inventory.md; the operator decision was recorded 2026-10-04 against OQ-ST-02 (docs/research/phase-0.md).

Decision

The project ships as a family of crates:

  1. alkstore (core) — the crate carrying the unified trait surface, types, error model, capability flags, and the contract documentation. No driver dependencies. Compile-lean by construction: the base crate has no engine machinery to keep out. (Annotated 2026-10-06: capability flags are not part of this contents list as decided — ADR-016 resolves there is no runtime capability surface, by default ever; the engine boundary lives at compile time and in the deployment matrix.)
  2. alkstore-sqlite — the SQLite engine implementing the core surface. Single driver: rusqlite + the forked honker-core lineage, carried in-tree as the engine crate's substrate module subtree ([ADR-003]; ownership per ADR-011; folded into the engine crate per ADR-013).
  3. alkstore-postgres — the Postgres engine implementing the core surface. Single driver: tokio-postgres + deadpool-postgres ([ADR-004]).
  4. A mem-shaped engine (in-tree or separate test crate) may exist as a third implementation for tests and doctests, if the test story turns out to want it. Treated as an implementation convenience, not a contract artifact — decided at implementation time, not now. (Superseded in part 2026-10-10 by ADR-024: the mem engine exists as the separate alkstore-mem crate — full contract v1, no durability, single-process, ephemeral by design, never fleet-valid, wasm32 compile-clean by acceptance. Not a test convenience, and not a core dev-dependency: core's doc examples stay mock-based.)

Downstream consumers depend on alkstore plus exactly one engine crate. A consumer binary links at most one driver.

Consequences

Positive

  • The engines' asymmetry of work is structural: the SQLite engine has a small, mostly-wiring job; the Postgres engine carries the build-heavy LISTEN/queue work. Neither is burdened by the other's dependencies.
  • Any future engine (alkfs's in-tree needs, an ops-surface engine) is additive — a new crate implementing the core traits — rather than a feature-graph edit to one crate.
  • Core-lean is structural, not a feature-discipline to be enforced by review.
  • The libsqlite3-sys link-collision class of problems is eliminated by construction (single-driver binaries).
  • Per-engine compilation/test gates are independent.

Negative

  • A version-coordination duty: the core's trait surface is a contract the engine crates must track. Version bumps in core must be adopted by engines in lockstep when the contract changes (minor/major discipline; no trait-default drift). (Discharged 2026-10-06: the duty's concrete shape is the versioning discipline — ADR-017, OQ-10's resolution: the core crate's semver is the contract version, change classes + lockstep duties pinned, the pairing tracked by manifest pin + version-stamped contract suite + docs; released engines on a breaking change keep their pinned majors — nothing forced.)
  • Slightly more crate plumbing; naming/publishing overhead.
  • Consumers who want both engines in one binary (rare, unsupported-by design) cannot get a both-features build today.

References

  • OQ-ST-02 (docs/research/phase-0.md) — the decision record with options considered.
  • docs/research/consumer-inventory.md — the inventory whose lean (single crate) was superseded, with the correction recorded there.
  • ADR-003 — the SQLite driver choice whose link-collision constraint motivates this split.
  • ADR-004 — the Postgres driver choice.
  • OQ-10 (docs/architecture/open-questions.md) — trait-versioning duties created here; resolved by ADR-017; OQ-11 — the fork-follow-through residue (per ADR-013 §4). [ADR-003]: 003-sqlite-driver.md [ADR-004]: 004-postgres-driver.md