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
5.1 KiB
5.1 KiB
id, name, status, depends_on, scope, risk, impact, level, tags
| id | name | status | depends_on | scope | risk | impact | level | tags | ||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| store-put-paths | Store put seam — two internal paths, the three numbered rules, Batch, kv-only rejection | pending |
|
moderate | medium | component | implementation |
|
Description
Implement the facade's put seam on the Store type (store-api.md
§Write path): put(len: Option<u64>, stream) → (Key, Pin) — two
internal paths behind one signature, with pin-before-publish from the
gc-protocol registry.
- Known-length path (encouraged, documented as such on the API):
the preamble (
"blob <len>\0") is known before content flows, so the hash initializes with it and one pass suffices — no staging needed for hashing. Routing by the dispatch seam: ≤ threshold → the kv tier's whole-value put; > threshold → the large tier's staged put (content streams into the staging cursor while hashing; the derivation completes at stream end; commit-rename publishes). - Unknown-length path: content buffers in memory up to the
dispatch threshold. ADR-003's three numbered rules (option (d)),
each a test-asserted behavior:
- Threshold exceeded mid-stream (non-restartable sources are normal): the buffered prefix flushes into a fresh stage file and the stream continues appending into it — there is no "restart the stream" requirement anywhere.
- Verification runs over the eventual stored artifact as a
whole: the memory buffer for the sub-threshold kv commit; the
complete staged bytes for the large commit — the derivation pass
restarts over preamble + staged bytes (forced by the preamble
being inside the hashed input, ADR-002), reading the staged
artifact back through the
StagingCursor: ReadCursorseam (the wave-1 design-led extension). There is no stitched-input case left ambiguous. - Stream error before commit: no entry lands and no stage file remains — buffer discard on the failure path, exactly like stage discard (the stage-hygiene invariant extends to the buffered case).
- Pin-before-publish (ADR-005): the pin refcount is acquired under
the arbitration lock before the entry becomes visible — publish
and protection are one step; an entry cannot appear in a sweep's
list()without already carrying its pin. - Dedup: a re-put of existing content acquires no new pool entry —
the returned
Pinrefcounts the existing one under the same lock. Batch(store-api.md §Write path): the batching put seam — multi-put writes group under one batch-scoped pin; member puts publish pin-before-publish exactly as lone puts do; the batch's pins drop together on commit or drop (thegc-protocolmachinery drives).- kv-only mode rejection (ADR-012 §6.2): known-length puts exceeding the threshold reject immediately; unknown-length puts reject at mid-stream threshold overflow — lengths are not known at construction, so put time is the only possible rejection point. Zero residue on either rejection.
- Named design points: (a) the stream parameter shape (
impl AsyncRead + Unpin + Sendvs a boxed trait object — the family's no-dyn posture vs ergonomics); (b) the declared-len mismatch case (a known-length stream that yields fewer/more bytes thanlen— which error family member carries it, and zero residue either way). Record both for the review gate.
Acceptance Criteria
put(len, stream) → (Key, Pin)with both internal paths behind the one signature; routing is the pure length function (no content ever migrates tiers)- The three numbered rules each behavior-tested: mid-stream overflow flushes into a fresh stage and the stream continues; the derivation pass runs over the eventual stored artifact as a whole (buffer form and staged-bytes form); stream error leaves no entry and no stage file
- Pin-before-publish holds on both paths and both tiers; dedup re-puts refcount the existing entry
Batchgroups multi-put writes; member pins drop together- kv-only mode: known-length over-threshold rejects immediately; unknown-length rejects at mid-stream overflow; zero residue on both
- Stream-shape and len-mismatch design points decided and recorded
cargo testgreen;cargo test --features memandcargo test --all-featuresgreen; clippy/fmt gates green
References
- docs/architecture/store-api.md (§Write path — the put seam, stage hygiene invariant)
- docs/architecture/decisions/003-backend-contract-and-dispatch.md (§Dispatch policy — the three numbered rules, option (d))
- docs/architecture/decisions/012-pre-decomposition-consistency-rulings.md §6.2 (kv-only put-time rejection)
- docs/architecture/decisions/005-pooled-cas-and-gc-mechanism.md (pin-before-publish, dedup refcount)
- docs/architecture/decisions/010-backend-io-seams-and-fleet-state-home.md §1 (the staged-put contract)
- src/backend.rs (StagingCursor: ReadCursor — the verification-pass seam)
Notes
To be filled by implementation agent
Summary
To be filled on completion