Files
alkblobs/docs/plans/implementation.md
T
glm-5.3-flash 9504893dc8 docs(review): wave 14 gate green — the family-wire surface verified against ADR-020's invariant
- §Decision 1's invariant walked clean per surface (facade write seam /
  key decode / wire ingress); the grep audit found no length-routed,
  default-derivation, or option-typed constructor; §Decisions 2/4/5/6/7
  verified line by line; the §Decision 8 litmus walked with one named,
  dispositioned residue (the schema gate's width enumeration rides a
  novel-width member's admission ADR — the ADR's own literal text, not
  the defect class)
- the Key::derive decomposition-time reading confirmed (the fold into
  the explicit Algorithm::derive; Key::from(Digest) the only
  Key-from-digest route)
- inline fixes: put_known_as's mangled duplicate doc sentences removed;
  the CHANGELOG's [0.2.0] section reordered newest-first (alkcall's
  convention)
- stamps: wave 14 completed in the implementation plan; AGENTS.md's
  status line updated (the superseded-shape warning retired) and its
  fuzzing paragraph's key_encoding target description aligned to
  ADR-020's shrunk surface
- the review task completed with the gate record in its notes

Verification: cargo test default 156/54/7/2; cargo test --all-features
server-less 341/125/7/3 (clean skips) and against the live pglo-poc
harness (the pg columns ran for real); cargo clippy --all-targets --
-D warnings and --all-features --all-targets -- -D warnings; cargo fmt
--check; cargo doc --no-deps; the fuzz corpus-replay gate 7/7; cargo
publish --dry-run --allow-dirty (0.2.0 packages)
2026-10-19 01:29:01 +00:00

55 KiB

status, last_updated
status last_updated
draft 2026-10-19 (wave 14 completed at its review gate — the ADR-020 implementation round landed and the gate green: the invariant walk (§Decision 1) clean per surface, the ADR's decisions verified line by line, the pinning test green with its wave-13-justification on record, the record corrections verified, the full verification block including the live pg harness and the fuzz corpus replay green, the publish dry-run green for 0.2.0. Earlier: wave 14 decomposed (2026-10-18) — six tasks along the ADR's §Decision 9 seams. Earlier still: wave 13 completed at the first-consumer gate — review-002's round landed (the alkcall 0.10 bump, C-1's git-object derivation family, C-2's token admission posture, the C-3/C-4 ride-alongs); the publish hold lifts. Earlier: wave 12 completed at the pre-release gate — the release-readiness wave's six tasks landed and the gate green: the docs honesty audit, the feature-graph matrix re-run, the full verification block including the fuzz corpus replay, and the publish dry-run; the release-facing specs flipped stable. Earlier still: wave 11 completed at the fuzzing gate. Earlier still: wave 10 completed at the security gate: S-1..S-7 landed and stamped on review-001, which is now closed with its residue named)

alkblobs — Implementation plan

Wave-based decomposition of the architecture (docs/architecture/) into units of implementable work. This is a deliberate deviation from the SDD process's decompose-everything-upfront step, adopting the sibling pattern proven on alkstore: the architecture is large, early waves change the shape of later ones (the seam shapes wave 1 pins are what every engine task implements against; the conformance suite's factory contract feeds every engine's verification work), and decomposing only the next wave at a time keeps each session's task set reviewable and lets later decompositions absorb earlier waves' course corrections.

Rhythm: decompose a wave → implement it → review gate → decompose the next wave. Task files live in tasks/ (frontmatter carries the categorical estimates). Wave boundaries are also review boundaries.

Scope authority: v0.1.0 ships the full surface — all five engines (kv: sqlite, postgres, mem; large: local, pg-lo), fleet GC, and the feature-gated ops module — per operator decision (2026-10-11): all downstream consumers need all of it, and the base crate must be fully usable and composable across mem/sqlite/postgres from the default build onward.

The waves

Dependency logic in one line: core contract → (mem ∥ local) → sqlite → postgres → pg-lo → fleet GC → ops → [split → correctness → security] → fuzzing → release readiness. The two dependency-free engines (mem, local) prove the entire facade + dispatch + in-process GC story before any SQL driver exists; sqlite and postgres are structurally independent of each other (both need only wave 2's trait + suite) and could run in either order — postgres is placed first of the two SQL engines because its wave establishes the full engine-state seam clause families that pg-lo (window family) and the fleet wave (the host) build on. pg-lo shares postgres's driver cluster and seam pattern. The fleet wave needs both SQL tiers landed: its canonical testable shape is kv=postgres + large=pg-lo on one instance (the composite fleet-validity predicate's joint entry+pin tx is expressible exactly there). The ops wave rides alkcall (published, 0.9) behind the default-off feature. Then the review-001 remediation waves (docs/reviews/001-storage-surface-general-review.md — the post-wave-7 whole-surface pass): the structural split first — the four god files carry 78% of the crate's loc, so they carry the bulk of the findings with it, and splitting first means every remediation after it lands in a small thematic module with its test next to the thing it tests — then the correctness round, then the security round. Fuzzing follows the remediation waves so its targets are chosen against the remediated surface; release readiness is last.

Wave Contents Depends on Status
1 Crate/feature scaffold; hashing & keys (the one-way door); error model; Backend trait + I/O seams + engine-state seam shape; facade value types; conformance-suite convention — decomposed (2026-10-11)
2 mem + local engines; facade put/read paths + dispatch + constructor modes; in-process GC protocol; suite's first columns wave 1 decomposed (2026-10-11)
3 sqlite kv engine (default): WAL posture, prepared statements, engine-state window family, suite column wave 2 decomposed (2026-10-11)
4 postgres kv engine: schema, pool, CAS rows, stable-cursor list, full engine-state clause families, suite column wave 2 decomposed (2026-10-12)
5 pg-lo large engine: LO staged put, descriptorless lo_get windows, companion rows, window-family seam clauses, suite column wave 4 decomposed (2026-10-12)
6 Fleet GC mechanism: DB-backed pins, joint entry+pin tx, sweep lease, SQL delete arbitration, fleet-validity predicate + constructor declarations waves 4 + 5 decomposed (2026-10-12)
7 ops feature: alkcall-backed op family, ACL mapping, pin tokens + renewal, fanout seam wave 6 decomposed (2026-10-12)
8 Review-001 remediation I: the structural split — store/, ops/, postgres/, pg_lo/ directory modules, re-exports unchanged; D-3 dead twins; dead-code allows; D-5 hygiene; mem default-on + the AGENTS verification matrix wave 7 completed (2026-10-16; gate green)
9 Review-001 remediation II: F-1..F-4 fixes with the failure/cancellation/contension regression tests (absorbing D-2's residue); the neighborhood smells (N-5/N-6 + the window batch at decomposition) wave 8 completed (2026-10-16; gate green)
10 Review-001 remediation III: S-2 bounds + streaming, S-4..S-7, S-1/S-3 per the OQ-14 resolution wave 9 completed (2026-10-15; gate green)
11 Fuzzing: family pattern targets (key encoding, ops payloads), corpus-replay gate wave 10 completed (2026-10-16; gate green)
12 Crate docs, feature-graph verification, README, publish prep wave 11 completed (2026-10-16; gate green)
13 Review-002 round: alkcall 0.10 bump; C-1 the git-object derivation family (ADR-018 + implementation incl. fleet threading); C-2 the token admission posture (ADR-019); C-3/C-4 ride-alongs wave 12 completed (2026-10-18; gate green — the publish hold lifts)
14 Review-003/ADR-020 round: the wire speaks keys (tag-prefixed key hex ingress + schemas); the facade's one put_as seam (put/put_git_object/from_git_oid_hex/Digest::derive deleted); the put handler under mandatory claims ({token, key} response); the pinning tests; the record corrections wave 13 completed (2026-10-19; gate green)

Wave 1 — Foundations

The contract surface in code, engine-blind: everything later waves implement against, and nothing that needs an engine to test.

  • Crate/feature scaffold — the ADR-013 §4 feature graph declared in the manifest from the first commit: sqlite default-on (rusqlite), postgres (tokio-postgres + deadpool-postgres), mem (dependency-free), pg-lo (the shared pg cluster; does not imply postgres — ADR-010 §1's consequence), ops (alkcall, default-off); local ungated. Engine modules ride #[cfg(feature)] from the start.
  • Hashing & keys (ADR-002 — the one-way door): Digest/Key types, the canonical git-blob-sha-256 derivation (H("blob <len>\0" + content)), tolerated git-blob-sha-1, the algorithm-tagged byte encoding (1 alg byte + digest; 0x01/0x02 assigned, 0x00 reserved), rejects-never-guesses validation, and the byte-exact git hash-object interop test (POC #1 findings 4/5's standard — validated against the git CLI, not just vectors).
  • Error model (store-api.md; ADR-012 §6.5): the one-enum error family with its altitude discipline — the trait's effective members (Missing/Verification/Io/KeyInvalid) and the facade's GcRefuse addition.
  • Backend trait + I/O seams (ADR-003/010): the trait method set (has/get/put/delete/list/name/size) with opaque byte keys; the read-cursor abstraction (kv materializing vs large streaming bodies; serves read_range's slice + slice-digest contract at one seam); the staged-put contract as a named per-engine shape (staging cursor handout, commit publishes, every failure path discards); and the crate-internal engine-state companion trait with its clause families and per-engine applicability (ADR-010 §2 as reduced by ADR-012 §5) — SQL engines implement it in waves 3-5; local/mem are excluded by construction.
  • Facade value types: EntryMeta, Pin/Batch (types + pinned lifecycle contract; the arbitration machinery is wave 2), SweepReport/GcAbortCause (abort-as-data, exactly three causes), PinState.
  • Conformance-suite convention — tests/conformance/ established with the row/stamp convention (ADR-§-stamped rows, the alkstore ADR-017 pattern adapted) and the factory contract (isolation + idempotent teardown); the invariant backlog from store-api.md registered as rows-to-grow. The compile-able harness lands with wave 2's first column (mem — the contract-reference engine).

The wave-1 review gate is the cheap-place gate: the key encoding is the declared one-way door (AGENTS convention 5) — a shape error found at this gate costs a task revision; found after an engine ships data it costs a migration ADR. The gate reads the encoding, the trait, and the seam shapes against the pinned ADR text line by line.

Wave 2 — The store works dependency-free (mem + local)

The first engines and the facade behavior, chosen so the whole store story is provable with zero driver dependencies:

  • mem kv engine (feature mem) — the contract-reference engine (ADR-011 §2): a BTreeMap-shaped implementation of the kv tier's contract including size; every other engine's conformance rows mirror its column.
  • local large engine (ungated) — the ADR-003 backend as specced: flat {hex-prefix}/{hex-prefix}/{hash} sharding, stage-then- commit-rename (the staged-put contract realized with its exact-count hygiene tests, POC #3 A5's shape), pread-loop read cursor, pread range reads.
  • Facade behavior — the put seam's two internal paths behind one signature (known-length one-pass; unknown-length pre-threshold buffering with the mid-stream stage flush and the restart-over- staged-bytes derivation pass, ADR-003's option (d) semantics pinned in three numbered rules); get/stat/read_range/has/delete/ list with the dispatch fall-through general rule (ADR-012 §6.1); size-threshold dispatch (pure function of length, default 128 KiB, constructor-tunable); constructor modes (dual-tier / kv-only with put-time rejection per ADR-012 §6.2 / mem-only) — kv=mem + large=local is the dispatch-coverage test shape ADR-012 §6.3 names.
  • In-process GC protocol (ADR-005) — pins with pin-before-publish ordering, registered liveness sources (live-shared), the protect callback with abort-as-data, delete windows (mark → arbitrate-at- delete-time → batched commit), sweep() returning Ok(SweepReport) always, direct-delete refusal (GcRefuse, the full protection set per ADR-012 §6.4), local/mem staging window candidates in-process and executing them inline in the same sweep (ADR-012 §5).
  • Suite columns — the harness compile-able and green on the mem and local columns: round-trips, dedup, stage hygiene, sweep-safety exact counts, virgin-store no-ops, stat-without-content.

Wave 2 decomposes after the wave-1 review gate, absorbing whatever the gate corrects.

Wave 3 — sqlite engine

The default kv engine (feature sqlite, default-on; REQ-1): WAL + synchronous=NORMAL as the shipped durability tier, prepared statements, has as an EXISTS probe, the indexed size column probe (POC #5 B5's row shape), virgin-store no-tables-yet tolerance, and the engine-state seam's window family only (ADR-012 §5's reduction — durable single-node window staging; no pin rows, no sweep-lock body). Suite column green server-less. The single-writer ceiling is a documented posture, not a defect (REQ-1's economics).

Wave 4 — postgres kv engine

The fleet-capable kv engine (feature postgres; REQ-2/REQ-3): schema bootstrap, pooled connections (tokio-postgres + deadpool-postgres) with per-connection prepared-statement discipline, ON CONFLICT DO NOTHING CAS rows, complete list() as a stable cursor over a vacuuming table, batch-delete via key = ANY($1), the synchronous_commit parity knob, and the engine-state seam's full clause families — postgres is the fleet host (ADR-012 §4): ensure_gc_schema (pins + window), pin_commit, pin_renew, pin_check/arbitrate, window_stage/window_take, sweep_lock_acquire/release (the pg advisory lock, the sweep lease's shipped provider). Suite column green against dockerized postgres (the alkstore harness convention: connection settings ride environment, tests skip cleanly server-less, acceptance requires green against the server). This wave establishes the seam implementation pattern pg-lo reuses.

Wave 5 — pg-lo large engine

The large tier's second engine (feature pg-lo; ADR-009, admitted on POC #7): Large Objects as storage — staged put as chunked lowrite statements (512 KiB per the POC's measured knee) inside a tx the store core holds, descriptorless lo_get(oid, off, len) window reads over the companion table (the C4 posture), companion-row size probes, transactional LO creation (zero crash orphans), and the seam's window family (durable window staging where the kv engine is not SQL-backed — the one-window-host rule). Suite column green against dockerized postgres. Named deltas documented (catalog space never returned; VACUUM FULL is the operator's shrink path).

Wave 6 — Fleet GC

The mechanism wave (ADR-008/010/012/013; REQ-2): DB-backed pins as expiry-carrying rows committed atomically with entries — the store core holds the joint entry+pin tx and engines contribute mechanics (pin_commit); the named exclusive sweep lease (one sweeper per pool, atomic test-and-set, holdership ends at release/owner death/session loss; contention is the SweeperLock report abort); SQL-level delete arbitration (SELECT … FOR UPDATE finger-lock at delete time); TTL/3 pin renewal on the guard; the composite fleet-validity predicate and the explicit fleet: true constructor declaration (never inferred); kv-only re-routing posture; the one-window-host rule under dual-SQL instances. Exact-count fleet sweep outcomes against live postgres — the wave's gate is the architecture's most intricate state machine proven under concurrency.

Wave 7 — Ops surface

The ops feature (ADR-001; alkcall 0.9): the op family — blobs/stat, blobs/have (with the token-renewal form), blobs/delete (internal authority), blobs/fetch (Sub), blobs/put (Sink, response {token, digest}) — JSON control plane via alkcall OperationSpec + JSON Schema validation, binary data plane via channel_open-allocated Sub/Sink channels; namespace-as-resource ACL mapping (closed-by-default per OQ-09); the pin hand-over + token renewal contract (the token is the pin; TTL/3 renewal via have/re-put; validity domain = minting node); the fetch fanout seam (one reader, store arm + subscriber arms; late joiners degrade to verified get). The module is a store-facade consumer with no backend side-door.

Wave 8 — The structural split (review-001)

Review-001's D-1 as the remediation round's first wave — deliberately before any finding is fixed: the four single files (store.rs 10,553, ops.rs 6,896, pg_lo.rs 3,089, postgres.rs 2,936) carry 78% of the crate, so they carry the bulk of the findings too; splitting first means the correctness and security waves' fixes are small reviewable diffs in thematic modules with each regression test next to the thing it tests, and D-2's failure-path tests get a per-module home instead of growing inside files that are themselves the problem. The wave is mechanical: every task is pure moves plus at most two small decision resolutions; no behavior change is intended anywhere; the public surface does not move (lib.rs re-exports unchanged — convention 10 requires modules, not flatness).

  • Directory splits — store.rs into store/ (the facade's put/read paths and constructor family; the engine-side enum dispatch; the fleet driver; the sweep executor; the GC glue and window plumbing; the test sections), ops.rs into ops/ (the token layer; the ACL mapping; the op specs + handlers + JSON Schemas; the put pipe; the fetch fanout; the wire-error synthesis; the in-process call harness; the e2e test tree), postgres.rs into postgres/, pg_lo.rs into pg_lo/ (pg_common.rs stays as it is). The internal module boundaries are each task's design points, recorded on the resulting module docs; the decomposition proposes boundaries, the task may adjust one with the note.
  • D-3 — the dead KvEngine::ensure_gc_schema/LargeEngine:: ensure_gc_schema twins (zero callers; docs name a consultant that does not consult them): preference is routing ensure_gc_schema_at_construction through the methods and deleting the re-implementation (one dispatch; honest docs); deletion of the methods + corrected construction docs is the accepted alternative. The chosen point recorded.
  • The blanket dead-code allows (the enabler that hid D-3 plus a false doc): removed from the split modules; any remaining allowance scoped to named items with their consumer recorded (the standing hygiene block's discipline).
  • D-5 hygiene — Digest::from_parts performs its documented validation (a debug_assert on bytes.len() == digest_len()); error.rs's tautological test replaced with a real assertion. The crate-root //! docs stay wave 12's (release readiness owns crate docs). (2026-10-15: the review's third item — hex to dev-dependencies — is retracted: the premise was false, hex is a runtime dependency of the key encoding, the local engine's sharded names, the pg key hexes, and the ops token/wire surfaces — see the correction on review-001 D-5.)
  • mem default-on + the AGENTS verification matrix (D-4/D-2b): mem joins default — it is dependency-free, so the default build's dependency tree is unchanged, and plain cargo test runs the contract-reference column (plus local's, which pairs kv=mem). ADR-013 §4 gains the dated amendment; AGENTS.md's verification block becomes the feature-aware matrix (what "green" means per shape: the default build's columns, the pg columns' ALKBLOBS_PG_* live-harness acceptance vs server-less skip posture, the two cargo-llvm-cov configurations recorded — the server-less number misleads, the live-harness number is the honest one).

Wave 9 — The correctness round (review-001)

The four correctness majors, unified by what hid them: the failure/cancellation/contension machinery nothing had ever exercised (D-2's correlation is the wave's working method — every fix ships with the tests that drive its previously-uncovered neighborhood). Depends on wave 8. Gated on OQ-12 (local durability posture, F-3) and OQ-13 (pg-lo co-tenancy, F-4) resolving to ADRs before decomposition — both are load-bearing contract decisions, recorded through the OQ/ADR path; the other items here are fix-shaped and need no decision.

  • F-1 — arbitrated_delete's SQL-consult error path runs conclude_delete before propagating (gc.rs's "every path" clause made true); regression tests: an injected arbitrate failure mid-delete asserting the key stays put-able and delete-able, and the Plan::Wait/protected contended-pin path exercised.
  • F-2 — PgJointTx gains a Drop guard aliasing PgLoStaging's spawned-ROLLBACK defense; the sweep lease's acquire/release cancellation windows get the same treatment; a cancel-the-future- mid-joint-tx test proves a subsequent checkout runs BEGIN/COMMIT cleanly. N-6's panic-safety Drop guards (the in-process lease and sweep_lock_release skipping on a panic inside the sweep body) ride here — same hazard class.
  • F-3 — per the OQ-12 resolution (ADR-015): the fsync-durable publish posture lands (file fsync before the publish, directory fsync after it) with the ADR's unconditional terms — the no-replace publish (the committed-file-never-overwritten invariant held by the filesystem), the error-on-truncation change (unix pread_all agrees with the non-unix read_exact path), and write-error-discards-stage.
  • F-4 — per the OQ-13 resolution (ADR-016): the orphan_reap_permitted constructor declaration (default false, refusing reap_orphans with a named error until declared), plus the ADR's unconditional riding fixes — the lo_entries(loid) supporting index, the already-vanished-loid tolerance, and the READ COMMITTED candidate-snapshot race fix.
  • The neighborhood smells — N-5 (put_in_batch's foreign-Batch debug_assert!, the one-line close), N-7 (delete_everywhere's partial failure reports which homes moved), and the window batch (N-2 transactionality, N-3 unbounded window_take, N-4 the backwards deadline move) kept or deferred at decomposition.
  • D-2's test round — the failure-path tests above plus the D-2(a) list's remainder: the op handlers' error arms (the malformed-payload sweep over the five ops), the failing-reader- through-fleet-put, the local engine's error family (walk_committed's fatal-malformed doctrine actually driven).

Decomposition record (2026-10-16): nine tasks — f1-arbitrate-conclude, f2-cancellation-guards (N-6 rides here, same hazard class), f3-local-durability (reads ADR-015), f4-reap-declaration (reads ADR-016), smells-n5-n7, window-family-smells, test-isolation-harness (filed 2026-10-16 from the f2 session's flake diagnosis — the cross-test pg_stat_activity observables, the harness-server capacity, the cadence tests' load tolerance; the application_name per-instance opt is the resolved design point — sequenced before the D-2 round, which depends on it), d2-failure-path-tests (after f3 and the isolation hardening — local.rs is shared, and the round's red/green signal needs the flaky ground fixed first), and review-wave-9. The window batch (N-2/N-3/N-4) is kept — small, correctness-adjacent, and no later wave owns them; N-5/N-7 land as one small pair task. The pg-touching tasks carry the live-harness env block in their text (ALKBLOBS_PG_HOST=localhost, ALKBLOBS_PG_PORT=15432, ALKBLOBS_PG_USER=postgres, ALKBLOBS_PG_PASSWORD=poc, ALKBLOBS_PG_DB=blobs — the local pglo-poc container, postgres:16-alpine on :15432); server-less runs skip cleanly, the live harness is the acceptance shape.

Wave 10 — The security round (review-001)

The ops and local security findings. Depends on wave 9 (sequenced, not coupled — the modules no longer overlap post-split, but the gates run in rhythm). Gated on OQ-14 (the ops authority model, S-1+S-3 as one model question) resolving to an ADR before decomposition.

  • S-2 — ranged fetch bounded: maxItems plus a total-bytes bound on the ranges schema; slices stream per-range from the pump instead of pre-materializing (the slice digests ride the ack as they already do). The one network-facing unbounded resource.
  • S-4 — internal error detail off the wire: stable domain texts at the CallError::internal/ingress synthesis points; the store core's Display detail (paths, DSN fragments, schema names) stays server-side.
  • S-5 — the have renewal form's token field gets maxLength/pattern before the proportional hex decode; the custodial-never-confirm warning and the token registry's process-lifetime retention recorded embedder-facing (an admission cap judged at decomposition, not silently absent).
  • S-6 — the local tier's dedup/stat paths stop following symlinks (symlink_metadata-based, or O_NOFOLLOW), one doctrine with walk_committed's loud rejection.
  • S-7 — the fanout residuals named on the module doc (broadcast-squatting under the unverified digest; the Stall policy's read-peg bounded by alkcall's channel lifecycle) so they read as decisions, not oversights.
  • S-1/S-3 — per the OQ-14 resolution (ADR-017): the CAS-global-pool authority model named (capability-to-ask, not data partitioning — per-namespace separation is ruled out by REQ-4 and OQ-BL-05) and the misleading error texts fixed to pool framing; renewal authority lands as namespace parity with the mint record; the write-only renewal duty lands as the documented remote-putter role requirement (read+write). The closed-by-default ACL surface and its e2e are unmodified.

Decomposition record (2026-10-16): six tasks — s1-s3-ops-authority-model (reads ADR-017; the model statement, the pool-framing texts, the renewal namespace parity with its outcome-mapping design point, the remote-putter role requirement — first, because the S-2/S-4/S-5/S-7 tasks all touch the files and doc sections it rewrites), s2-ranged-fetch-bounds (the maxItems + total-bytes bounds with their values/homes as design points, the per-range pump streaming with the double-read tradeoff named — the digests still ride the ack, so establishment reads per-range transiently), s5-token-field-bounds (the token field bounded before the decode; the registry/custodial retention recorded embedder-facing; the admission cap judged at decomposition: deferred-and-documented — the registry is process-lifetime by decided design, growth requires an authenticated write-granted principal looping puts without confirm, and a cap is an eviction/refusal policy decision on the mint contract, not security-round material; the cap is named as the future hardening, with a judgment-flip escape if the implementation session finds it trivially expressible), s4-wire-error-detail (stable domain texts at every synthesis point; the detail's server-side disposition a design point — the crate has no logging dependency, the lean default is dropped-detail recorded), s6-local-symlink-doctrine (independent — local.rs only; the post-F-3 no-replace publish shifted the finding's premise, so the task verifies the publish arm loud-by-test and lands the stat/get doctrine), s7-fanout-residuals-doc (doc-only; sequenced behind s1-s3 and s2 because they edit the same doc sections), and review-wave-10 (the security gate). The pg-touching tasks (s1-s3's fleet renewal forms; the review's pg columns) carry the live-harness env block in their text (ALKBLOBS_PG_HOST=localhost, ALKBLOBS_PG_PORT=15432, ALKBLOBS_PG_USER=postgres, ALKBLOBS_PG_PASSWORD=poc, ALKBLOBS_PG_DB=blobs — the local pglo-poc container, postgres:16-alpine on :15432); server-less runs skip cleanly, the live harness is the acceptance shape.

Wave 11 — Fuzzing

The family pattern (alkcall/alktunnels/alksocks/alktty/alkstore), per operator standing decision that anything worth fuzzing gets fuzzed: the fuzz/ sidecar workspace (nightly pinned inside fuzz/ only, the stable fuzz/shared corpus-replay gate, the detached runner), targets over the key-encoding parser (Key::from_bytes — the one-way door's reject-never-guess discipline), the ops payload surfaces (JSON specs + binary channel framing, if wave 7's shapes warrant it), and any put-path staging logic the surface review names (review-001's finding regions — the failure/cancellation machinery, the remediated ops surfaces — are the target inventory's first read). Committed crash-input seeds; grown corpora gitignored; the corpus-replay gate joins the verification list when the wave lands. Targets are chosen at decomposition time against the then-current surface — the wave exists in the plan as a commitment, not yet a target inventory.

Decomposition record (2026-10-15): six tasks — fuzz-scaffold (the sidecar workspace copied from alkstore, the most recent sibling; run-detached.sh byte-identical — cmp-verified identical across alkcall/alksocks/ alkstore at decomposition — the family's OOM-isolation rule: campaigns never run as a foreground child of an agent session, the setsid detach so a target's OOM cannot kill the agent's process tree), fuzz-ingress-exposure (the #[doc(hidden)] narrow surface for the ranged-fetch bounds ingress — fetch_ranges_ingress + FetchRange/RangedBounds widened in place, the alkstore fuzz-parser-exposure precedent; a fuzz-only shim rejected as drift risk, the harness route unavailable — #[cfg(test)]), fuzz-target-key-encoding (the one-way door's parser cluster — Key::from_bytes, Digest::from_git_oid_hex, the registry mapping, derive determinism; all public API, no exposure), fuzz-target-ops-ingress (the alkblobs-owned post-schema ingress — the S-2 ranged-fetch bounds arithmetic and the S-5 token decode; the JSON-spec schema gate itself is alkcall's surface, family-fuzzed there), fuzz-campaigns-and-gate (detached campaigns, findings disposition, the corpus policy, the AGENTS.md verification-list flip), and review-wave-11 (the fuzzing gate).

The target inventory, fixed at this decomposition against the remediated surface (review-001's finding regions were the first read; the closed review's N-8..N-14 observations were read too — none names a fuzz target, they are hygiene/doc items remaining recorded for wave-12 candidacy):

  • key_encoding — the one-way door's parser cluster. The plan's first named cluster, taken whole.
  • ops_ingress — the ops payload surfaces, resolved: the JSON specs' alkblobs-owned delta is the post-schema ingress (the schema gate is alkcall's, already family-fuzzed; the in-process harness is #[cfg(test)], unavailable to the sidecar) — the S-2 bounds arithmetic and the S-5 token decode are exactly the network-facing parse surfaces worth fuzzing.
  • Binary channel framing — dispositioned out of scope. No alkblobs-owned codec exists: the fetch/put pumps write raw bytes over alkcall's BiStream, the ack rides establishment reply fields (alkcall's JSON), the in-band error frame is ADR-049's alkcall machinery. The framing surface is alkcall's, fuzzed in its own wave. Nothing to target here.
  • Put-path staging logic — dispositioned out of scope. The staging machinery's hazard class is concurrency/cancellation (the tee pipe, the staged-put windows, the derivation pass) — the libFuzzer byte-input model does not reach it; that class is covered by wave 9's deterministic failure-path regression tests (F-1/F-2, the D-2 round). The put path's parse pieces (the offer's digest ingress) ride the two targets above.

Wave 12 — Release readiness

Crate docs (public-API rustdoc warning-free; the facade's docs are contract text), the feature-graph verification pass (every coherent engine-feature combination compiles and passes its gates — pg-pure builds, pg-lo without postgres, --all-features), the root README (written last, honestly), publish prep (package metadata, cargo package verification per the exclude list, path-dep/version bounds), and the pre-release review gate flipping the release-facing specs to stable.

Decomposition record (2026-10-16): seven tasks — unicode-hygiene (first, operator finding: gitea's ambiguous-unicode banner flags exactly three characters present in the repo's prose — en-dash U+2013, multiply sign U+00D7, logical-or U+2228, checked against gitea's modules/charset/generate/ambiguous.json confusable tables; the house typography — em-dash, §, arrows, ellipsis, box-drawing — is not flagged and stays; surgical ASCII replacements repo-wide, the rule recorded in AGENTS.md), docs-consistency-review (operator finding: the whole-docs drift round — the known-stale spots named at decomposition are the architecture README's Current State (still says wave 8 decomposed / 9-10 pending), AGENTS.md's status line (still "ADRs 001-006"), and review-001's N-8..N-14 observations, which wave 11 named "wave-12 candidacy" — each dispositioned, plus the general sweep: frontmatter accuracy, post-split path references, cross-links), crate-docs (the crate-root //! docs wave 8 deferred here, plus the public-API rustdoc pass — warning-free on both doc builds), feature-graph-verification (the coherent matrix per ADR-013 §4: default, --all-features, pg-pure, pg-lo without postgres, pg-pure+pg-lo, mem-only, local-only, ops over the lean base; results recorded for the review to re-run), changelog-and-readme (operator finding: the CHANGELOG — [0.1.0], the first entry, in the alkcall/alkstore Keep-a-Changelog form — and the root README, written last and honestly, after the docs and matrix work so it describes verified reality), publish-prep (operator finding: the manifest audit — the exclude list is missing fuzz/ against the alksocks family set, readme = "README.md" is absent against alkcall/alktty/alksocks; then cargo package file-list verification and cargo publish --dry-run), and review-wave-12 (the pre-release gate: docs honesty audit, the matrix re-run, the full verification block including the fuzz corpus replay, the publish dry-run, and the release-facing specs flipped stable).

Sequencing: unicode-hygiene first (it edits the prose every later task reads); docs-consistency-review, crate-docs, and the feature-graph verification then run (the first two after unicode, the third independent); changelog-and-readme after all three (it describes verified reality); publish-prep after the README exists (the readme field wires it); the review gate last.

Wave 13 — The first-consumer round (review-002)

The publish hold lifts here. Wave 12's gate left the crate release-ready but unpublished, by operator decision, pending the first downstream consumer's review — and that review landed (docs/reviews/002-first-consumer-review.md, from alkgit's three recomposition POCs: POC-L the LFS client over the ops surface, POC-O this crate as a git object store, POC-R the co-tenant registry composition). C-1 and C-2 are the publish-hold reasons; C-3 rides the same round; C-4 is a ride-along. The endorsed sequencing (operator, 2026-10-17): the alkcall 0.10 bump first (small, independent); C-1 as its own ADR-then-implementation session; C-2 as its own decision-and-landing session; C-3/C-4 as ride-alongs; publish after C-1 and C-2 land.

  • alkcall 0.10 bump — the gateway ownership-seam release (alkcall review 012, downstream evidence from alkgit's POC-L): additive API plus the loud ownership default. alkblobs is a producer on the native wire with providers wired; alkcall's survey found no impact — the manifest pin plus a full gate re-run is the expected whole change.
  • C-1 — the git-object derivation family (DQ-1a, POC-O-validated): registry bytes 0x03/0x04, the preimage-identity derivation (the loose-object form <type> <len>\0 + content stored as the value; the digest is the plain hash over it — byte-identical to git's oid, every kind included), put_git_object, key decode/order, and the fleet threading the prototype branch skipped. The prototype branch poc-o-git-objects (f0b045c) is the working reference to re-implement from, not to merge. Decision-shaped: the ops kind question (from_git_oid_hex's 40/64-hex ingress is ambiguous across families once non-blob forms share the digest space), the stat = stored-preimage-length semantic, ADR-002's annotated exception (preimage-identity hashing fits the domain-separation invariant only as an explicit exception), the namespace posture (git oids are global; ADR-017's caveat stated for the family), and the public put shape. OQ-15 → ADR-018, then the implementation.
  • C-2 — the token registry's admission posture. Review-001's S-5 deferred the admission cap as "a mint-contract policy decision"; POC-L now supplies the deciding input it was waiting for — the concrete attack shape (a non-fleet store's unconfirmed custodial token holds its pin indefinitely, registered per mint, no cap on mints; a hostile LFS-style upload loop is unbounded pin-holding plus unbounded registry growth). The decision space: the cap at the mint boundary, the fleet-form TTL posture made available to non-fleet deployments, or the documented embedder-responsibility posture — the review's bar is "not silence." OQ-16 → ADR-019, then the landing.
  • C-3 — sqlite busy_timeout explicit (ride-along): the contention posture joins the durability posture as stated contract (knob or pinned constant; today it rides rusqlite's implicit 5 s default, coincidentally equal to alkstore's explicit 5000 ms).
  • C-4 — &Key borrowed-key overloads (ride-along, nit): the serving paths' per-call as_bytes() allocation dropped where ownership is not needed; the borrowed shape is a design point (the tag byte is not stored adjacent to the digest bytes).

Decomposition record (2026-10-17): seven tasks — alkcall-010-bump (first, independent), c1-adr-git-object-family (OQ-15 → ADR-018; docs/ADR only — the five decisions, the spec updates, the prototype's deviations named), c1-git-object-family-impl (after the ADR and the bump; the family lands incl. the fleet threading, the interop tests, the fuzz-target consistency), c2-token-custody-posture (OQ-16 → ADR-019; after the bump — it lands in the ops module), c3-sqlite-busy-timeout (independent, sqlite.rs only), c4-borrowed-key-overloads (after c1-impl — keys.rs and local.rs:899 are shared touch-points), and review-wave-13 (the gate that lifts the publish hold). The pg-touching runs carry the live-harness env block per AGENTS.md; server-less runs skip cleanly.

Wave 14 — The family wire round (ADR-020)

Review-003's implementation round: the coherence defect it recorded — the store holds an entry class the network op family cannot see — closes per ADR-020 (accepted 2026-10-18; the review's addressing conversation D-1..D-6 verbatim). The ADR is the gate; nothing lands ahead of it, and it is already accepted — this wave decomposes directly against it, the first time a wave has had its decision ADR in hand before decomposition. The round is deliberately one flat decomposition, not a multi-wave split: the design is fully decided (no open OQ gates it), the touched surface is small (five files carry the change; the store layer takes no changes per ADR-020 §Decision 9 — review-003's scope note stands verified), and the ADR itself names the decomposition seams and their order. The wave-pattern rationale (large scope; early waves changing later shapes) does not apply; what carries over from the rhythm is the review gate — here reading the landed surface against ADR-020 §Decision 1's invariant, the durable fix F-2.3 asked for ("the next gate has something to push against").

The seams, in the ADR's §Decision 9 order (each task names its anchors; the chain is linear — the facade task deletes Digest::from_git_oid_hex, which the ops module must already be off, so the wire seam lands first):

  • The wire speaks keys — digest_ingress re-pins to the physical key encoding (tag-prefixed key hex, 42/66 chars, a Key::from_bytes-shaped decode; bare hex dies), the schema gate becomes ^[0-9a-f]{42}$|^[0-9a-f]{66}$, gate and ingress change together (S-5), the INVALID_DIGEST texts describe the family-explicit contract. Read ops (fetch/stat/have/delete) work for family entries from this task alone — the store already serves family keys; the write side waits for the put-handler task (intermediate state: a wire-offered family put rejects honestly at the put handler's blob-hardcoded cross-check — no wrong landing is possible).
  • The facade's one seam — put_as(algorithm, len: Option<u64>, stream) alone; put, put_git_object, from_git_oid_hex, and Digest::derive deleted outright (0.2.0, no shim); put_in_batch takes the algorithm; the put.rs:360 re-hardcode fixed; Key::derive — the same silent canonical constructor one wrapper up, unnamed by the ADR's §Decision 5 sentence but decided by its §Decision 1 invariant (never a hidden default at an address-forming boundary) — folds with Digest::derive.
  • The put handler under mandatory claims — the offer's key hex + len both mandatory; the tee hasher parameterizes per the offered key's algorithm and always opens at stream start; the store arm routes put_as; the deferred post-check arm deleted (claim checking has exactly one moment — pre-EOF); the response echoes the key ({token, key}).
  • The pinning tests — POC-X's tests/ops_gap.rs substance re-homed: a facade-published family entry fetchable, stat-able, and have-servable over the ops surface; a wire-offered family put landing under its own oid.
  • The record corrections — ops-surface.md, store-api.md, hashing-and-keys.md, the ops module docs, the README, and fuzz/README.md drop the blob-only/wave-13 surface language; the CHANGELOG's [0.2.0] entry and the version bump ride here.

Decomposition record (2026-10-18): six tasks — wire-key-hex-ingress (first — the facade task deletes from_git_oid_hex, which the ops module must already be off), facade-put-as-seam (the public write surface unified; all in-repo callers swept incl. the fuzz key_encoding target's surface), ops-put-handler-key-claims (depends on both preceding — the offer/response shapes and the handler body), pinning-tests-family-wire (the gap reproduction re-homed as the pinning test), record-corrections-adr-020 (docs describe landed reality; the 0.2.0 bump), and review-wave-14 (the family-wire gate). No pg-touching code changes are expected (the store layer takes no changes) — the full verification block still runs per AGENTS.md, the live-harness pg columns included.

Gate record (2026-10-19, review-wave-14): the wave landed green — §Decision 1's invariant walked clean per surface (facade write seam, key decode, wire ingress; the grep audit found no length-routed or default-derivation constructor), §Decisions 2/4/5 verified line by line against the landed code, §Decision 6/7 verified (reads untouched; five ops, no family-suffixed variants), the §Decision 8 litmus walked (one admitted residue: the schema gate's width enumeration is a one-line touch under a novel-digest-width member's admission ADR — the defect class it is not), the Key::derive decomposition-time reading confirmed (the fold into Algorithm:: derive; Key::from(Digest) remains, fully explicit), the pinning test green with its wave-13 justification on record, the record corrections verified, and the full verification block green server-less and against the live pg harness — the publish dry-run green for 0.2.0.

Decided points

  • Wave-based decomposition (operator-endorsed, 2026-10-11): the SDD decompose-everything-upfront step is replaced by the alkstore-proven rhythm — decompose the next wave after the previous wave's review gate; later decompositions absorb earlier outcomes.
  • v0.1.0 scope — full surface (operator, 2026-10-11): all five engines, fleet GC, and the ops module ship in the first release; the base crate is fully usable and composable across mem/sqlite/postgres by default.
  • Engine order: mem + local first (the contract-reference engine plus the ungated engine — the dependency-free pair proves facade, dispatch, and in-process GC end-to-end, and kv=mem + large=local is the ADR-012 §6.3 dispatch-coverage shape); then sqlite (default), postgres (establishes the full seam), pg-lo (shares the pg cluster
    • seam pattern), fleet GC last of the storage waves (its canonical testable shape needs both SQL tiers).
  • Contract-suite home: in-crate tests/conformance/ (operator, 2026-10-11) — engines are features of one crate, not sibling crates, so a separate suite crate buys nothing; the harness is factory-parameterized over engine constructors, rows ADR-stamped (the alkstore ADR-017 convention adapted), mem the reference column.
  • Fuzzing: adopted family-wide as wave 8, after ops and before release (operator standing rule: fuzz anything worth fuzzing; it almost always pays for itself). Target inventory is fixed at that wave's decomposition against the matured surface.
  • Single crate, engine features (ADR-013 §4): no engine-crate workspace split; the feature graph is declared in the manifest from wave 1; feature-gated engines' gates run under --all-features.
  • Review-001 remediation inserted; fuzzing/release renumbered 11/12 (operator, 2026-10-14): docs/reviews/ 001-storage-surface-general-review.md (the post-wave-7 whole-surface pass) was past the inline-fix bar, so three remediation waves insert before fuzzing/release — the structural split first (the god files are most of the crate's loc and carry the bulk of the findings with it), then the correctness round, then the security round — renumbering fuzzing to wave 11 and release readiness to wave 12. The original numbering was a guide, not a contract; pre-renumbering records ("wave 8 (fuzzing)" in tasks/review-wave-7.md and older task files) read as their current numbers. The remaining review rounds keep the rhythm: decompose → implement → gate, one wave at a time.
  • mem flips default-on (operator, 2026-10-14): the contract-reference conformance column (plus local's, which pairs kv=mem) runs in plain cargo test — dissolving review-001 D-4 at the root rather than documenting around it. The feature is dependency-free, so the default build's dependency tree is unchanged; ADR-013 §4 gains the dated amendment (wave 8's task), and AGENTS.md's verification block becomes the feature-aware matrix (D-2b).
  • Decision-shaped findings route through OQs and ADRs (operator, 2026-10-14): F-3 → OQ-12, F-4 → OQ-13, S-1+S-3 → OQ-14 (one model question, decided together). Their resolutions gate the relevant waves' decompositions (waves 9 and 10), not wave 8's — the split needs no decisions. The remaining findings are fix-shaped; review-001 is their record, no ceremony.
  • OQ-12/13/14 resolved (operator, 2026-10-15, ADR-015/016/017): the three decision-shaped findings decided on evidence in hand — ADR-015: the local engine's durability posture is fsync-durable publish (file fsync before the commit-rename + directory fsync; no-replace publish; error-on-truncation; no constructor knob); ADR-016: one large-object consumer per pg database, enforced as the orphan_reap_permitted constructor declaration gating reap_orphans (default refuses); ADR-017: the ops authority model is the CAS global pool — a namespace grant is capability-to-ask, not data partitioning (separation = separate stores); renewal authority is namespace parity with the mint record, and the remote-putter role's explicit duty is read+write. Waves 9 and 10 are decomposable now: F-3's task reads ADR-015, F-4's reads ADR-016, and wave 10's S-1/S-3 task reads ADR-017.
  • Review-002's decision-shaped findings decompose with their ADR tasks inside wave 13 (operator-endorsed sequencing, 2026-10-17): a deliberate variation on the review-001 pattern (where OQ-12/13/14 had to resolve to ADRs before the wave decomposed). Here OQ-15 and OQ-16 open at decomposition and resolve in-wave — the ADR task precedes and gates its implementation task (c1-impl depends on c1-adr; c2 is its own decision-and-landing unit), which preserves the decision-before-code discipline without a separate pre-wave pass. Justification: the publish window is open now, the wave is small, and the consumer is waiting on the outcomes.
  • CI: none, deliberately (family posture): CI and publishing run manually; the merge gates (cargo test, cargo clippy --all-targets -- -D warnings, cargo fmt --check, plus --all-features variants once features carry code) are run coordinator-side.

Review gates

Each wave ends in a review task (review-wave-N) before the next wave decomposes. Specific gates:

  • Wave 1 review — the one-way-door gate: the key encoding read against ADR-002 line by line (a shape error here is a task revision; after engines ship data it is a migration ADR); the trait + seam shapes against ADR-003/010; the error model's altitude split against store-api.md/ADR-012 §6.5; the feature graph against ADR-013 §4; the named design points (single-enum error reading, Backend visibility, trait put-form shapes, oid hex policy, async-trait shape) audited as resolved.
  • Wave 2 review — facade + dispatch + GC vs ADR-003/005/012 (the three numbered unknown-length rules, the delete-window ordering invariants, abort-as-data); stage-hygiene exact-count tests; the suite's first columns green on mem + local; the dispatch-coverage shape exercised.
  • Engine wave reviews (3/4/5) — engine-vs-contract conformance: the suite column green (server-less for sqlite; vs dockerized postgres for the pg engines), the engine-state clause families contract-tested per applicability, virgin-store semantics, size metadata-only discipline.
  • Wave 6 review — the fleet gate: exact-count sweep outcomes under concurrency vs live postgres, the joint entry+pin tx atomicity, the sweep lease semantics vs ADR-013 §2, the composite fleet-validity predicate enforced at the constructor, kv-only put-time rejection.
  • Wave 7 review — ops-vs-alkcall conformance (the machinery ridden, not re-invented); ACL mapping vs OQ-09's closed-by-default; the pin token contract vs ADR-010 §4/ADR-012 §2; no backend side-door.
  • Wave 8 review — the split gate: each split diff audited as pure moves plus its named resolution(s) and nothing else, lib.rs's re-export set unchanged (the public API does not move), full suites green across the feature matrix (default — now carrying the mem column — server-less --all-features, and the live-harness pg acceptance), the clippy/fmt/doc variants green, D-3 resolved with honest docs, the dead-code allow inventory clean, review-001's frontmatter stamped (its file:line references predate the split).
  • Wave 9 review — the failure-path gate: the F-1..F-4 fixes green with their regression tests (the arbitration wait/protected neighborhood, the cancellation-mid-tx checkout check, the failing-reader-through-fleet-put, the local error family driven), OQ-12/OQ-13's resolutions recorded as ADRs and matching the landed behavior, a live-harness cargo-llvm-cov run diffed against review-001's uncovered list showing the residue absorbed, no uncovered regression introduced.
  • Wave 10 review — the security gate: S-2's bounds verified by test (an over-bound request rejected at the schema, the bounded-request streaming byte-verified), S-4/S-5/S-6 fixed with their tests, S-7's residuals named on the module, S-1/S-3 consistent with the landed OQ-14 ADR, the closed-by-default ACL surface unchanged (the existing ACL matrix e2e green unmodified).
  • Wave 11 review — campaigns run clean (or findings fixed + promoted to seeds), the corpus-replay gate green on stable, seeds committed per the corpus policy.
  • Wave 12 review — the pre-release gate: docs honesty audit, the feature-graph verification re-run, all gates green (including the fuzz replay), release-facing specs flipped stable.
  • Wave 13 review — the first-consumer gate: C-1 verified end to end (ADR-018's five decisions owned; the family landed per it; the one-way door's bytes read against the ADR line by line — the wave-1 gate's discipline at the door's last cheap moment), C-2's "not silence" bar met (ADR-019 + the attack shape on record), C-3/ C-4 as chartered, review-002 stamped closed, the full verification block green, the publish dry-run green. On green, the publish hold lifts (publishing runs manually, coordinator-side).
  • Wave 14 review — the family-wire gate: the landed surface read against ADR-020 §Decision 1's invariant (the durable fix — every address-forming or address-matching surface requires the algorithm explicitly: the facade's put_as, the key decode, the wire ingress; no hidden default, no length inference anywhere), then line by line: the wire form per §Decision 2 (tag-prefixed key hex; bare hex dead; gate and ingress agree), the put handler per §Decision 4 (mandatory claims, one claim-check moment, the echoed key), the facade per §Decision 5 (one seam; the deleted surfaces absent from the public API), reads unchanged per §Decision 6 (no read signature moved), the op set closed per §Decision 7 (no new ops), the §Decision 8 litmus walked (the VFS admission is one byte and one ADR), the pinning test green, the full verification block green (the fuzz corpus replay included — the key_encoding target's surface shrank), cargo publish --dry-run --allow-dirty green for 0.2.0, the CHANGELOG current, the plan and AGENTS.md stamps.