Nine tasks against the wave-1 contract surface as reviewed (no amendments from the gate): - engine-mem / engine-local (parallel, dependency-free): the two engines; local owns the stage-hygiene exact-count tests and the Drop-cancellation story the wave-1 review recorded - store-construction: the Store type, constructor modes, size-threshold dispatch; binds the conformance factory's Store associated type - store-read-paths ∥ gc-protocol: read ops with the ADR-012 §6.1 fall-through rule; the ADR-005 in-process protocol (pins, liveness, delete windows, abort-as-data, GcRefuse with the full protection set) - store-put-paths: the two put paths behind one signature, ADR-003's three numbered rules, pin-before-publish, Batch, kv-only rejection - suite-column-mem → suite-column-local: the reference column, then the dual-tier dispatch-coverage shape mirroring it - review-wave-2: the gate (facade/dispatch/GC vs ADR-003/005/012) Verification: dependency graph validated (all ids resolve, no cycles, topological order clean); docs-only change. Refs: docs/plans/implementation.md §Wave 2
88 lines
4.1 KiB
Markdown
88 lines
4.1 KiB
Markdown
---
|
|
id: store-read-paths
|
|
name: Store read paths — get/stat/read_range/has/list with dispatch fall-through
|
|
status: pending
|
|
depends_on: [store-construction]
|
|
scope: moderate
|
|
risk: medium
|
|
impact: component
|
|
level: implementation
|
|
tags: [wave-2, facade, read-path]
|
|
---
|
|
|
|
## Description
|
|
|
|
Implement the facade's read-side ops on the `Store` type (store-api.md
|
|
§Read path, §Lifecycle), behind the dispatch seam from
|
|
`store-construction`. Direct `delete` is *not* here — it lands in
|
|
`gc-protocol` with its arbitration.
|
|
|
|
- **The fall-through general rule** (ADR-012 §6.1; POC #1 finding 6):
|
|
every key-addressed op consults the kv tier first, then the large
|
|
tier. `get`/`stat`/`has`/`read_range`/`list` all follow it.
|
|
- **`get(key) → Option<(len, stream)>`**: small tier returns a bounded
|
|
read; large tier streams. The per-engine cursor shapes stay hidden
|
|
behind the crate-internal `ReadCursor` the store core drives
|
|
(ADR-010 §1) — **named design point**: the public stream type that
|
|
wraps the cursor (the facade's `(len, stream)` shape is uniform
|
|
across tiers; the cursor is never exposed). A miss on both tiers is
|
|
`None` — `get` never carries `Error::Missing` (ADR-012 §6.5 ruling 5;
|
|
the error module's behavioral-enforcement note lands here).
|
|
- **`stat(key) → Option<EntryMeta>`**: the cheap probe riding the
|
|
trait's `size` method — length plus algorithm, never a content load
|
|
(ADR-008's size discipline). Fall-through per the general rule.
|
|
- **`read_range(key, range) → (slice, slice_digest)`**: slice plus the
|
|
out-of-band SHA-256 slice digest (ADR-006, fixed by convention —
|
|
composed through the cursor's `read_range` seam). `Err(Missing)` on a
|
|
both-tier miss. Range reads fall through tiers exactly as `get` does.
|
|
- **`has(key) → bool`**: the pure existence probe (ADR-008's `has`
|
|
discipline) — a size-probe under the fall-through rule; absent on
|
|
both tiers is `false`; virgin-store reads are no-ops.
|
|
- **`list() → stream of keys`**: whole-pool enumeration, complete by
|
|
contract. **Named design point**: the return shape — store-api.md
|
|
says "stream of keys"; decide Vec vs stream (the sweep consumes the
|
|
same enumeration crate-internally) and record it.
|
|
- **Verification posture on get** (named design point): ADR-006's
|
|
whole-blob verification vs the slice-digest-only reading — where the
|
|
store core's hash-check actually fires on the read path (kv
|
|
materialized reads are verifiable cheaply; large streaming reads ride
|
|
per-range slice digests). Pin the reading against ADR-006's text and
|
|
record it for the review gate.
|
|
- **`KeyInvalid` at the boundary**: typed `Key` conversion happens at
|
|
the store boundary only (ADR-002/003) — malformed key bytes on any
|
|
read op reject with `Error::KeyInvalid`, never a guess; engines never
|
|
see typed keys.
|
|
|
|
## Acceptance Criteria
|
|
|
|
- [ ] `get`/`stat`/`read_range`/`has`/`list` implemented with the
|
|
fall-through general rule (kv first, then large), unit-tested
|
|
including the both-tier-miss shapes (`None` vs `Err(Missing)` per
|
|
op)
|
|
- [ ] The public stream type decided and recorded; engine cursors never
|
|
leak into the public surface
|
|
- [ ] `stat` never fetches content (rides `size`); `EntryMeta` carries
|
|
len + algorithm
|
|
- [ ] `read_range` returns slice + SHA-256 slice digest per the ADR-006
|
|
convention; verification-on-get posture decided and recorded
|
|
- [ ] Malformed key bytes reject with `KeyInvalid` at the facade
|
|
boundary on every key-addressed op
|
|
- [ ] `cargo test` green; `cargo test --features mem` and `cargo test
|
|
--all-features` green; clippy/fmt gates green
|
|
|
|
## References
|
|
|
|
- docs/architecture/store-api.md (§Read path, §Lifecycle, §Error model)
|
|
- docs/architecture/backends-and-dispatch.md (§Size-threshold dispatch — get fall-through)
|
|
- docs/architecture/decisions/012-pre-decomposition-consistency-rulings.md §6.1/§6.5
|
|
- docs/architecture/decisions/006-verification-posture-and-transfer-encoding.md
|
|
- docs/architecture/decisions/008-trait-size-probe-fleet-gc-and-large-engines.md (size discipline)
|
|
- src/backend.rs (the ReadCursor seam this drives)
|
|
|
|
## Notes
|
|
|
|
> To be filled by implementation agent
|
|
|
|
## Summary
|
|
|
|
> To be filled on completion |