Files
alkblobs/tasks/review-wave-14.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

16 KiB

id, name, status, depends_on, scope, risk, impact, level, tags
id name status depends_on scope risk impact level tags
review-wave-14 Wave 14 review — the family-wire gate (the invariant is the thing the gate pushes against) completed
wire-key-hex-ingress
facade-put-as-seam
ops-put-handler-key-claims
pinning-tests-family-wire
record-corrections-adr-020
moderate low project review
wave-14
review-003
adr-020
gate

Description

The wave-14 review gate. Review-003's F-2.3 named the failure mode this gate exists to correct: the wave-13 gate validated the bytes against the record while nothing validated the record against the invariant. ADR-020 §Decision 1 is that invariant, stated as record precisely so this gate has something to push against. The gate reads the landed surface against the invariant first, then the ADR's decisions line by line:

  • The invariant (§Decision 1) — every address-forming or address-matching surface requires the algorithm explicitly, never an option, never a hidden default, never length-implied. Walk all three: the facade's write seam (put_as — the algorithm is a required parameter; no default constructor remains at any level, Digest::derive and Key::derive included), the key decode (Key::from_bytes — tag validated, per-tag length checked), the wire ingress (tag-prefixed key hex — the tag is in the payload, length is a check). Grep-audit: no length-routed or default-derivation constructor survives in the public API.
  • The wire form (§Decision 2) — tag-prefixed key hex everywhere; bare hex dead; the schema gate ^[0-9a-f]{42}$|^[0-9a-f]{66}$; gate and ingress agree (S-5); the INVALID_DIGEST texts describe the family-explicit contract.
  • The put handler (§Decision 4) — the offer's key + len both mandatory; the tee hasher parameterized per the offered key's algorithm, always opening at stream start; the store arm routes put_as; the post-check arm deleted (one claim-check moment, pre-EOF); the response echoes the key ({token, key}); renewal/ confirm/have semantics unchanged.
  • The facade (§Decision 5) — one seam; put, put_git_object, from_git_oid_hex, Digest::derive absent from the public API (and Key::derive per the decomposition-time invariant reading — the gate confirms or contests that reading explicitly); the put.rs:360 threading honored; the unknown-length arm retained with the identity-family restart-reuse form.
  • Reads unchanged (§Decision 6) — no read signature moved (get/stat/list/has/delete/read_range); the asymmetry sentence carried in the ADR is reflected, not re-litigated.
  • The op set closed (§Decision 7) — five ops, no family-suffixed variants; the correction is payload-parameterization only.
  • The litmus (§Decision 8) — walk it: a hypothetical VFS registry member's entire ask of the landed surface is a registry byte under its own ADR — no wire change, no facade change, no new method. Any per-consumer touch found is the defect class re-entering; name it.
  • The pinning test — green, both family members, the bare-hex negative included; the would-have-failed-on-wave-13 justification on record.
  • The record corrections — no doc advertises a deleted shape or the superseded posture (the grep sweep); the CHANGELOG's [0.2.0] entry current; the version at 0.2.0.
  • The full verification block green — cargo test (default), cargo test --all-features server-less and against the live pg harness (ALKBLOBS_PG_HOST=localhost, ALKBLOBS_PG_PORT=15432, ALKBLOBS_PG_USER=postgres, ALKBLOBS_PG_PASSWORD=poc, ALKBLOBS_PG_DB=blobs), both clippy variants, cargo fmt --check, cargo doc --no-deps, the fuzz corpus-replay gate (the key_encoding target's shrunken surface).
  • Publish readiness — cargo publish --dry-run --allow-dirty green for 0.2.0 (publication runs manually, coordinator-side — the gate does not publish).
  • The stamps — wave 14 marked completed in docs/plans/implementation.md; AGENTS.md's status line updated (the ADR-020 wave landed; the superseded-shape warning retired — the code no longer carries the wave-13 shapes).
  • Residue named — anything the round surfaced without a home gets one (an OQ, a task, or an explicit not-ours), not a shrug.

Acceptance Criteria

  • The invariant walk recorded per surface (facade / key decode / wire ingress), the grep audit clean
  • §Decision 2/4/5 verified line by line against the landed code
  • §Decision 6/7 verified (reads untouched; the op set closed)
  • The §Decision 8 litmus walked and recorded
  • The Key::derive decomposition-time reading confirmed or contested explicitly (contested → the operator decides before the stamp)
  • The pinning test green with its justification on record
  • The record corrections verified (the docs grep sweep clean; CHANGELOG/0.2.0 current)
  • Full verification block green, including the fuzz corpus-replay gate
  • cargo publish --dry-run --allow-dirty green for 0.2.0
  • docs/plans/implementation.md wave 14 stamped completed; AGENTS.md's status line updated
  • Any residue named with a home

References

  • docs/architecture/decisions/020-family-explicit-wire-payloads.md (the gate's contract — §Decision 1 first)
  • docs/reviews/003-family-wire-gap-review.md (F-2.3 — the failure mode this gate's shape corrects)
  • docs/plans/implementation.md (wave 14 charter; the review-gates section)
  • tasks/wire-key-hex-ingress.md; facade-put-as-seam.md; ops-put-handler-key-claims.md; pinning-tests-family-wire.md; record-corrections-adr-020.md
  • AGENTS.md (the verification block; conventions 5 and 12)

Notes

The invariant (§Decision 1) — walked clean per surface.

  • Facade write seam: put_as(algorithm, len: Option<u64>, stream) (src/store/put.rs:132) and put_in_batch(batch, algorithm, len, stream) (src/store/put.rs:181) are the only public write methods; put_known_as (pub(crate)) and put_unknown take the threaded Algorithm at every arm, the fleet arms included — the put.rs:360 re-hardcode is gone (put_unknown passes its parameter to fleet_put_pglo_unknown, pinned by fleet_pglo_unknown_arm_honors_the_threaded_algorithm, src/store/fleet.rs:2583). No matches! on Algorithm anywhere in src/store/ or src/ops/.
  • Key decode: Key::from_bytes (src/keys.rs:92) — the tag validated via Algorithm::try_from_byte, the digest length checked per tag (digest_bytes.len() != algorithm.digest_len()), never inferred. The only other constructor surface is Digest::from_parts (pub(crate), the from_bytes internals).
  • Wire ingress: digest_ingress (src/ops/wire.rs:82) — explicit lowercase-charset gate (S-5: the ingress accepts nothing the [0-9a-f] schema gate rejects), hex decode, then Key::from_bytes verbatim. Bare 40/64-hex dies at the schema gate (^[0-9a-f]{42}$|^[0-9a-f]{66}$, wire.rs:168).
  • Grep audit: zero hits for from_git_oid_hex / put_git_object / Key::derive / Digest::derive / Option<Algorithm> across src/, tests/, fuzz/shared/src/; no == 40 / == 64 length-routed checks in the ops/store/keys/hashing surfaces. The facade's remaining .put(...) call sites are all the engine-level Backend trait seam (opaque byte keys) — no facade-level put survives.

§Decision 2 verified: tag-prefixed key hex across stat/have/ delete/fetch/put-offer (the shared digest_wire_schema/ digests_wire_schema and the put offer's key field all gate on DIGEST_WIRE_PATTERN); the INVALID_DIGEST declared texts (src/ops/specs.rs:52/152/299, fetch.rs, wire.rs:97) all state "tag-prefixed key hex (lowercase, 42 or 66 chars)" — the family-explicit contract, no blob-family hex-length language.

§Decision 4 verified: the offer schema requires namespace, key, len (src/ops/put.rs:80-88); put_establish and put_handle both refuse a missing key/len pre-ingress (post-schema-unreachable guards); BlobHasher::new(offered_key.algorithm(), offered_len) opens unconditionally at stream start (put.rs:346) — no offered_len.is_some() gating survives; the store arm routes put_as(offered_key.algorithm(), Some(offered_len), reader) (put.rs:331); finish_put carries no cross-check arm and no offered-digest parameters — the pre-EOF cross-check (put.rs:386-394) is the one claim-check moment, running before the facade's EOF (the kill-before-commit + DIGEST_MISMATCH settle); the response is {token, key} with tag-prefixed hex::encode(key.as_bytes()) (put.rs:503-506); renewal/confirm/have semantics untouched (their ops files changed only their doc vocabulary).

§Decision 5 verified: one seam; the deleted names absent from the public API (grep-clean above); the unknown-length arm retained — preamble members restart over preamble + staged bytes, the identity-family restart-reuse pinned by unknown_length_identity_family_put_lands_the_known_length_key (src/store/tests.rs:1756); the seam-guard transformation pinned (put_as_blob_algorithm_lands_under_the_canonical_derivation, tests/git_interop.rs:478 — the old put's behavior is the seam's blob arm); put_in_batch takes the algorithm and routes put_as.

§Decision 6 verified: get/stat/list/has/delete/ read_range signatures unchanged (src/store/read.rs, sweep.rs) — reads name an existing address, the asymmetry carried, not re-litigated.

§Decision 7 verified: five ops — blobs/stat, blobs/have, blobs/delete (specs.rs), blobs/fetch (channels/blobs/sub), blobs/put (channels/blobs/pub) — the harness stubs are test-only; no family-suffixed op names exist.

§Decision 8 litmus — walked; one residue named. A new registry member's ask of the landed surface: an Algorithm variant + registry byte + digest_len/derive arms under its own ADR (the registry itself — the admitted ask); Key::from_bytes, digest_ingress, put_as, and every read op go generic with zero changes. The one enumerated touch: DIGEST_WIRE_PATTERN (src/ops/wire.rs:168) hardcodes the widths 42|66 (and digest_ingress's message text repeats them) — a member reusing the 20/32-digest widths takes zero wire change; a member with a novel digest width needs a one-line pattern extension alongside digest_len's new arm. Disposition (named, not the defect class re-entering): this is ADR-020 §Decision 2's own literal text (the ADR pins ^[0-9a-f]{42}$|^[0-9a-f]{66}$ verbatim), so the landed shape is the record — and the extension rides the member's admission ADR in the same ceremony as digest_len's arm, a bounded one-line touch in one file, not a per-consumer scoping or a consumer-named shape. If a member with a novel width ever files, its ADR carries that line; no separate OQ opened (the admission ceremony is the home).

The Key::derive reading — confirmed, not contested. §Decision 1's invariant ("never a hidden default at an address-forming boundary") decides the Key-level form along with §Decision 5's Digest::derive sentence: both silent canonical wrappers fold into the explicit Algorithm::derive(algorithm, content, len) (src/hashing.rs:157), and Key::from(Digest) (src/keys.rs:161) remains as the only Key-from-digest route — fully explicit, nothing hidden. No caller, test, or doc needed the deleted forms (the sweep was mechanical); the reading is consistent and the operator's confirmation is not required to close the wave.

The pinning test — green with its justification on record. a_facade_published_family_entry_is_wire_readable_end_to_end (src/ops/tests.rs:2034), parameterized over both family members (GitObjectSha1 tag 0x03/42-char, GitObjectSha256 tag 0x04/66-char, from one shared loose preimage — the cross-family vector in-crate): facade put_as → oid-identical digest asserted, wire stat (ADR-018 §Decision 3's stored-preimage length), wire have (present: [true]), ranged fetch with slice-digest verification, a wire-offered put landing under the claimed oid (echoed key == offer, custody, token), the landed entry then serving stat + whole fetch. The bare-hex negative rides the malformed-wire-sweep battery (schema-gate INVALID_INPUT for the put and stat shapes) and the ingress-guard test (handler-direct INVALID_DIGEST). The recorded justification: on the wave-13 surface the have leg answered present: false (the ingress length-routed the bare oid onto a blob-family key) and the test's tag-prefixed offer failed the 40/64 schema gate before any decode — the test could not have passed.

The record corrections — verified. The repo-wide grep sweep for the deleted names as current-surface claims: every remaining hit is a historical record (ADRs, reviews, open-questions, the plan's wave text) or a deletion note; src/ops/ docs, the README, fuzz/README.md, tests/conformance/README.md all describe put_as + tag-prefixed key hex. CHANGELOG's [0.2.0] entry present with both breaks and the ADR-020 ref (the review moved it above [0.1.0] for the newest-first ordering alkcall's file follows — the one inline correction); Cargo.toml at 0.2.0.

Inline fixes taken by this gate (minor, reasonable): put_known_as's doc comment carried a mangled duplication of its closing sentences (src/store/put.rs) — deduplicated; the CHANGELOG's [0.2.0] section sat below [0.1.0] — reordered newest-first. Also the gate's own stamps (AGENTS.md status line + the fuzzing paragraph's stale target description; the plan's wave-14 stamp).

Full verification block — green, re-run at this gate (post inline-fixes): cargo test default (156 unit / 54 conformance / 7 git-interop / 2 readme), cargo test --all-features server-less (341 / 125 / 7 / 3, clean skips) and against the live pglo-poc harness (341 / 125 / 7 / 3 — the pg columns ran for real, 88.67s and 96.17s wall times), cargo clippy --all-targets -- -D warnings and cargo clippy --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, upload aborted per dry run).

Residue named: only the §Decision 8 width-enumeration note above (dispositioned into the member-admission ceremony). Nothing else the round surfaced lacked a home.

Summary

The wave-14 gate reads green against ADR-020's invariant. §Decision 1 walked clean per surface — the facade's one put_as/ put_in_batch write seam with the derivation threaded through every internal arm (the put.rs:360 re-hardcode fixed and fleet-pinned), the key decode's tag-validated per-tag-length check, and the wire ingress's tag-prefixed-key-hex contract behind the 42/66 schema gate (the S-5 agreement pinned by test); the grep audit found no length-routed, default-derivation, or option-typed constructor surfacing anywhere. §Decisions 2/4/5 verified line by line (mandatory {namespace, key, len} claims, the tee hasher opened unconditionally at stream start under the offered key's algorithm, the store arm routing put_as, exactly one pre-EOF claim-check moment with the post-check arm deleted, the {token, key} response); §Decision 6/7 verified (read signatures untouched, the five-op set closed); the §Decision 8 litmus walked with one named, dispositioned residue (the schema gate's width enumeration rides a novel-width member's admission ADR — ADR-020's own literal text, not the defect class). The Key::derive decomposition-time reading is confirmed: both silent constructors fold into the explicit Algorithm::derive, Key::from(Digest) the only Key-from-digest route. The pinning test is green for both family members with the wave-13 failure mode recorded (the have leg's present: false was the wall). Record corrections verified repo-wide; CHANGELOG/0.2.0 current (section reordered newest-first inline). The full verification block — including the live pg harness columns and the fuzz corpus replay — plus the 0.2.0 publish dry-run are green. Wave 14 stamped completed; AGENTS.md's status line retired the superseded-shape warning.