- §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)
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:
sqlitedefault-on (rusqlite),postgres(tokio-postgres + deadpool-postgres),mem(dependency-free),pg-lo(the shared pg cluster; does not implypostgres— ADR-010 §1's consequence),ops(alkcall, default-off);localungated. Engine modules ride#[cfg(feature)]from the start. - Hashing & keys (ADR-002 — the one-way door):
Digest/Keytypes, the canonicalgit-blob-sha-256derivation (H("blob <len>\0" + content)), toleratedgit-blob-sha-1, the algorithm-tagged byte encoding (1 alg byte + digest; 0x01/0x02 assigned, 0x00 reserved), rejects-never-guesses validation, and the byte-exactgit hash-objectinterop 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'sGcRefuseaddition. - 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; servesread_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/memare 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): aBTreeMap-shaped implementation of the kv tier's contract includingsize; 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/listwith 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=localis 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()returningOk(SweepReport)always, direct-delete refusal (GcRefuse, the full protection set per ADR-012 §6.4),local/memstaging 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.rsintostore/(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.rsintoops/(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.rsintopostgres/,pg_lo.rsintopg_lo/(pg_common.rsstays 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_schematwins (zero callers; docs name a consultant that does not consult them): preference is routingensure_gc_schema_at_constructionthrough 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_partsperforms its documented validation (adebug_assertonbytes.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 —hexto dev-dependencies — is retracted: the premise was false,hexis 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):
memjoinsdefault— it is dependency-free, so the default build's dependency tree is unchanged, and plaincargo testruns the contract-reference column (pluslocal'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 runsconclude_deletebefore propagating (gc.rs's "every path" clause made true); regression tests: an injectedarbitratefailure mid-delete asserting the key stays put-able and delete-able, and thePlan::Wait/protected contended-pin path exercised. - F-2 —
PgJointTxgains a Drop guard aliasingPgLoStaging'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 runsBEGIN/COMMITcleanly. N-6's panic-safety Drop guards (the in-process lease andsweep_lock_releaseskipping 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_allagrees with the non-unixread_exactpath), and write-error-discards-stage. - F-4 — per the OQ-13 resolution (ADR-016): the
orphan_reap_permittedconstructor declaration (default false, refusingreap_orphanswith a named error until declared), plus the ADR's unconditional riding fixes — thelo_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-Batchdebug_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 unboundedwindow_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:
maxItemsplus a total-bytes bound on therangesschema; 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'sDisplaydetail (paths, DSN fragments, schema names) stays server-side. - S-5 — the
haverenewal form'stokenfield getsmaxLength/patternbefore 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, orO_NOFOLLOW), one doctrine withwalk_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 branchpoc-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), thestat= 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_timeoutexplicit (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 —
&Keyborrowed-key overloads (ride-along, nit): the serving paths' per-callas_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_ingressre-pins to the physical key encoding (tag-prefixed key hex, 42/66 chars, aKey::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), theINVALID_DIGESTtexts 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, andDigest::derivedeleted outright (0.2.0, no shim);put_in_batchtakes 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 withDigest::derive. - The put handler under mandatory claims — the offer's key hex +
lenboth mandatory; the tee hasher parameterizes per the offered key's algorithm and always opens at stream start; the store arm routesput_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.rssubstance 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
opsmodule 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=localis 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 plaincargo 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_permittedconstructor declaration gatingreap_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-impldepends onc1-adr;c2is 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-featuresvariants 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,
sizemetadata-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 — thekey_encodingtarget's surface shrank),cargo publish --dry-run --allow-dirtygreen for 0.2.0, the CHANGELOG current, the plan and AGENTS.md stamps.