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

180 lines
9.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: store-construction
name: Store facade type — constructor modes and size-threshold dispatch
status: completed
depends_on: [engine-mem, engine-local]
scope: moderate
risk: medium
impact: project
level: implementation
tags: [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.