Files
alkblobs/tasks/store-construction.md
T
glm-5.3-flash 0fb451f945 docs(unicode-hygiene): gitea-flagged confusable characters out of repo prose
- en-dash (U+2013) -> ASCII hyphen across src/tests/docs/tasks
- multiply sign (U+00D7) -> x in the same prose
- logical or (U+2228) -> slash-list "(pins / markers / sources)" in
  src/gc.rs and its prose mirrors (gc-protocol.md, review-wave-2.md)
- task file's self-referencing mentions rewritten to codepoint/ASCII
  phrasing; fuzz/target and fuzz/corpus never touched
- rule recorded as AGENTS.md convention 11

Verification:
- cargo test (149+54+4), cargo test --all-features (328+125+4) green
- cargo clippy --all-targets / --all-features -D warnings clean
- cargo fmt --check clean; fuzz/shared corpus-replay gate green
- git grep -nP '[\x{2013}\x{00D7}\x{2228}]' over tracked files: empty
2026-10-16 08:31:59 +00:00

9.7 KiB

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.