Files
alkblobs/tasks/store-construction.md
T
glm-5.3-flash 532adfa707 feat(store): the Store facade — constructor modes, size-threshold dispatch, the factory binding
The wave-2 facade skeleton and routing (tasks/store-construction.md):
the public surface consumers program against, with the ops methods
still to land against it in store-read-paths/gc-protocol/store-put-paths.

- src/store.rs — the public `Store` type (engine-blind, ADR-007/008/
  010) over crate-internal tier enums (`KvEngine`{mem}, `LargeEngine`{
  local}): the engine-representation design point decided as concrete
  + enum dispatch, generic rejected (pub(crate) engines, no dyn);
  the three constructor modes as named constructors — `mem_only`,
  `kv_only_mem`, `dual_tier_mem_local` (the ADR-012 §6.3
  dispatch-coverage shape) — with `StoreMode` introspection; the
  write-side routing seam (`tier_for`), the read-side fall-through
  order (`kv()` then `large()`, ADR-012 §6.1), and the `GcHome`
  GC-state field; the ADR-012 §3 fleet declaration ruled deferred to
  the engine waves (nothing fleet-valid is constructible yet)
- src/dispatch.rs — the pure routing function (`route`, at-or-below
  → kv), `DEFAULT_DISPATCH_THRESHOLD` (128 KiB) exported at the
  crate root, boundary-value unit tests (exactly-at-threshold,
  degenerate tunings, purity)
- src/gc.rs — `GcHome`: the store-instance GC-state home the
  gc-protocol task fills (in-process; fleet state stays on the
  engine-state seam per ADR-010 §2/ADR-012 §4)
- tests/conformance — the wave-2 binding made as anticipated: the
  factory's `Store` associated type collapsed into the concrete
  `alkblobs::Store` (`open` vends it directly), recorded in
  factory.rs + README; the wave-1 mock probe retired, replaced by
  mem-gated binding demo factories (binding.rs) over both
  constructible shapes

Verification: `cargo test` (default), `cargo test --features mem`,
`cargo test --all-features` green (41/53/53 unit + conformance
targets green in all three shapes); `cargo clippy --all-targets
-- -D warnings` green in all three feature shapes; `cargo fmt
--check` green; `cargo doc --no-deps` adds no warnings beyond the
pre-existing private-intra-doc-links class.
2026-10-11 22:18:32 +00:00

9.7 KiB
Raw Blame History

id, name, status, depends_on, scope, risk, impact, level, tags
id name status depends_on scope risk impact level tags
store-construction Store facade type — constructor modes and size-threshold dispatch completed
engine-mem
engine-local
moderate medium project implementation
wave-2
facade
dispatch

Description

Introduce the Store facade type (store-api.md §What this is) — the public surface consumers program against — with its constructor modes and the size-threshold dispatch. This task builds the skeleton and routing: the ops methods land in store-read-paths, gc-protocol, and store-put-paths against this type.

  • The Store type (public, src/facade.rs or a new module — implementer's call, one module per file): holds the kv-tier engine and the large-tier engine (or none, per mode) plus the GC state home the gc-protocol task fills. Named design point — engine representation: a concrete Store with a crate-internal enum dispatching over the closed five-engine set, vs a generic Store<Kv: Backend, Large: Backend>. The conformance factory's binding note (tests/conformance/factory.rs) anticipates the concrete type ("most plausibly the associated type collapsing to the concrete facade type across all factories"); engines are statically known and in-crate (no dyn anywhere, wave-1 design point 3). Decide, record, and keep the public surface engine-blind (ADR-007/008/010: which engines a node runs changes nothing here).
  • Constructor modes (ADR-011 §4; ADR-012 §6.3):
    • dual-tier (the default): kv + large, size-threshold dispatch. Wave-2 constructible shape: kv = mem + large = local — the dispatch-coverage test shape ADR-012 §6.3 names (both tiers load-bearing).
    • kv-only: no large tier; over-threshold puts reject at put time (ADR-012 §6.2 — the rejection behavior itself lands in store-put-paths; this task carries the mode and its validation).
    • mem-only: the mem engine alone, no dispatch threshold in effect; never production.
    • Mode machinery is built so the wave-3–5 engine constructors slot in without reshaping (sqlite/postgres/pg-lo constructors are later waves; only feature-gated constructors exist when their feature is on).
  • Size-threshold dispatch (ADR-003): routing as a pure function of content length — same content ⇒ same length ⇒ same tier, no content ever migrates. Default threshold 128 KiB (constructor-tunable; the conservative edge of the measured ~128–256 KiB crossover zone, POC #3 A4). The dispatch function is pure and unit-tested (boundary values: exactly-at-threshold routing).
  • The dispatch seam the ops tasks program through: tier selection for a given length, and the fall-through order (kv first, then large) the read paths and delete consume (ADR-012 §6.1's general rule).
  • Conformance factory binding: bind StoreFactory::Store to the real facade type (the skeleton's anticipated collapse to the concrete type); if binding reveals a shape correction, make it and record it in factory.rs + tests/conformance/README.md per the skeleton's note.

Acceptance Criteria

  • Public Store type with the three constructor modes; engine representation decided and recorded (enum-dispatch vs generic) with the public surface engine-blind
  • Size-threshold dispatch: pure function of length, default 128 KiB, constructor-tunable; boundary-value unit tests (at/below/above threshold route deterministically)
  • kv = mem + large = local dual-tier construction works (the ADR-012 §6.3 dispatch-coverage shape); kv-only and mem-only modes construct and validate
  • StoreFactory::Store bound to the real facade; any shape correction recorded in factory.rs + README
  • cargo test green (default build: local ungated); cargo test --features mem and cargo test --all-features green; clippy/fmt gates green in all three feature shapes

References

  • docs/architecture/store-api.md (§What this is; §Public surface)
  • docs/architecture/backends-and-dispatch.md (§Size-threshold dispatch; §Constructor modes)
  • docs/architecture/decisions/003-backend-contract-and-dispatch.md (§Dispatch policy)
  • docs/architecture/decisions/011-vocabulary-tiers-engines-and-fleet-state.md §4
  • docs/architecture/decisions/012-pre-decomposition-consistency-rulings.md §6.2/§6.3
  • tests/conformance/factory.rs (the binding note)

Notes

  • Engine representation decided: concrete Store holding two crate-internal engine enums (KvEngine: mem [feature-gated]; LargeEngine: local [ungated]), not the generic Store<Kv, Large> — the generic would leak pub(crate) engine types through the public surface or force dyn (wave-1 design point 3); the facade stays engine-blind (ADR-007/008/010) and the conformance factory's binding note anticipated exactly this collapse. Waves 3–5 slot in by adding an enum variant + a named (feature-gated) constructor — no reshaping. Recorded in src/store.rs module docs.
  • Constructor modes land as named constructors, engines named by the constructor (public surface can't take pub(crate) engine handles): mem_only() / kv_only_mem(threshold) / dual_tier_mem_local(root, threshold) (the ADR-012 §6.3 dispatch-coverage shape; fallible because the local root's creation is I/O). Modes validate structurally (unrepresentable states unrepresentable — dual-tier constructs the large engine itself) and every instance carries its mode (Store::mode() → StoreMode, public). The fleet declaration (fleet: true, ADR-012 §3) defers to the engine waves: it is a constructor error for every non-fleet-valid configuration, and none is constructible yet — the ruling is recorded in store.rs docs rather than carried as a dead-machinery flag.
  • Threshold shape: None-vs-value is the mode's ruling (mem-only = "no dispatch threshold in effect", ADR-011 §4), so the field is always a value and mem-only's threshold() returns None by mode branch — no unrepresentable Option state to expect away (AGENTS convention 2). The pure function is dispatch::route(len, threshold) with the pinned boundary reading at-or-below → kv; DEFAULT_DISPATCH_THRESHOLD (128 KiB) is exported at the crate root and constructors take the threshold explicitly (tunability proven by tests at a non-default threshold).
  • Dispatch seam handed to the ops tasks: write-side Store::tier_for(len) (dual-tier pure routing; kv-only routes kv for every length — the over-threshold rejection is the put seam's behavior keyed off threshold(), ADR-012 §6.2; mem-only kv), and read-side fall-through order as kv()-then-large() accessors documented as ADR-012 §6.1's general rule. The engine enum's Backend dispatch methods and the cursor/staging enum wrappers are deliberately not built here — they are store-read-paths and store-put-paths named design points (the public stream shape, the declared-len mismatch case) and adding methods to the enums is slotting in.
  • GC state home: pub(crate) struct GcHome in src/gc.rs, held as a Store field with a crate-internal gc() accessor — the gc-protocol task fills it (pin registry, liveness sources, protect callback, in-process window); fleet state explicitly never lands there (ADR-010 §2 / ADR-012 §4 host rule).
  • Factory binding made and recorded: the skeleton's Store associated type is gone — StoreFactory::open vends the concrete alkblobs::Store directly (tests/conformance/factory.rs + README §The factory contract record the ruling); the wave-1 mock probe retired, replaced by mem-gated binding demo factories (tests/conformance/binding.rs) covering mem-only and the dispatch-coverage dual-tier shape with isolation + idempotent teardown.
  • Default-build note: the kv engine set is empty until wave 3 (sqlite), so Store exists in default builds but is unconstructible — constructors and construction tests are #[cfg(feature = "mem")]; the #![allow(dead_code)] scope notes on dispatch.rs/store.rs follow the house pattern and shrink as the ops tasks land.

Summary

Implemented the Store facade skeleton in src/store.rs: the public engine-blind facade type holding the two crate-internal tier enums (KvEngine {mem}, LargeEngine {local}) per the recorded engine-representation ruling (concrete + enum dispatch, generic rejected), the three constructor modes as named constructors — mem_only(), kv_only_mem(threshold), dual_tier_mem_local(root, threshold) (the ADR-012 §6.3 shape) — with StoreMode introspection, the write-side routing seam (tier_for) composing the pure dispatch::route (default 128 KiB, constructor-tunable, at-threshold → kv), the read-side fall-through order (kv() then large(), ADR-012 §6.1), and the GcHome GC-state field the gc-protocol task fills (added to src/gc.rs). dispatch.rs grew the pure routing function plus DEFAULT_DISPATCH_THRESHOLD with boundary-value unit tests (exactly-at-threshold, degenerate tunings, purity). The conformance factory binding collapsed the Store associated type into the concrete facade (recorded in factory.rs + the suite README); the wave-1 probe became mem-gated binding demo factories. Tests: mem-only/kv-only/ dual-tier construction + validation, threshold tunability at a non-default value, dispatch boundary values, factory open/teardown lifecycle in both constructible shapes.

Verification: cargo test (default), cargo test --features mem, cargo test --all-features all green; cargo clippy --all-targets -- -D warnings green in all three feature shapes; cargo fmt --check green; cargo doc --no-deps clean of new warnings beyond the pre-existing private-intra-doc-links class.