Files
alkblobs/tasks/core-error-model.md
T
glm-5.3-flash fc5ab57d18 feat(errors): the one-enum error family with altitude discipline
- src/error.rs: thiserror Error enum with the five ADR-012 \u00a76.5
  variants (Missing, Verification, Io(String), GcRefuse, KeyInvalid);
  doc comments carry store-api.md \u00a7Error model's semantics and the
  one-enum/two-altitude discipline (trait's effective members are
  Missing/Verification/Io/KeyInvalid; facade re-exposes with GcRefuse)
- no #[non_exhaustive] per the ADR-pinned variant set; Error derives
  Debug/PartialEq/Eq
- lib.rs re-exports Error
- unit tests: variant construction, the single-enum altitude shape,
  and the Missing-is-never-get/stat-absence doc contract (facade
  behavioral enforcement lands in wave 2)
- tasks/core-error-model.md: status completed; Notes + Summary filled

Verified: cargo test, cargo clippy --all-targets -- -D warnings,
cargo fmt --check
2026-10-11 18:10:04 +00:00

4.7 KiB

id, name, status, depends_on, scope, risk, impact, level, tags
id name status depends_on scope risk impact level tags
core-error-model Error model — the one-enum family with altitude discipline completed
scaffold-crate-features
single low component implementation
wave-1
errors

Description

Implement the crate's error family (src/error.rs, re-exported from lib.rs) per store-api.md's error model and ADR-012 §6.5:

  • One Error enum (thiserror) with exactly the five distinguished variants: Missing, Verification, Io(String), GcRefuse, KeyInvalid.
  • The altitude discipline in doc comments (store-api.md's "one enum, both altitudes"): the Backend trait's methods return this family but its effective members are Missing/Verification/Io/KeyInvalid — KeyInvalid never fires at the trait boundary (keys arrive opaque there) and GcRefuse never fires below the facade (arbitration answers flow up as data; the facade decides and owns the refusal). The facade's public surface re-exposes the family with GcRefuse added.
  • Semantics per variant: Missing — key absent on ops that signal absence via Err (read_range, delete); get/stat signal absence with None and never carry Missing. Verification — put/get hash-check failure (content ≠ key). Io(String) — backend media failure, stringly because std::io::Error is not stable across versions. GcRefuse — direct delete refused (protected key: pinned, registered-source-live, or protect-callback-named). KeyInvalid — malformed key bytes at the boundary (truncated, unknown algorithm — rejects, never guesses).
  • Named design point for the review gate: store-api.md's wording ("one enum, both altitudes… the trait's effective members… the facade re-exposes them with GcRefuse added") is implemented as a single enum with the altitude discipline carried by doc comments and construction discipline (not two types). The gate audits this reading against the spec text; if the gate reads it as requiring two types, that is a cheap fix now.

No panics; no unwrap/expect outside tests; #[non_exhaustive] is not applied unless the spec requires it (the variant set is ADR-pinned; new failure families are new ADRs — match the family's posture on alkstore's error taxonomy).

Acceptance Criteria

  • Error enum with the five variants, thiserror-derived Display/Error impls
  • Doc comments on the enum and each variant stating the semantics and the altitude discipline verbatim-traceable to store-api.md
  • Unit tests: each variant constructs; Missing documented get/stat exclusion stated in docs (behavioral enforcement lands with the facade in wave 2)
  • cargo test, cargo clippy --all-targets -- -D warnings, cargo fmt --check green

References

  • docs/architecture/store-api.md (§Error model)
  • docs/architecture/decisions/012-pre-decomposition-consistency-rulings.md §6.5, §1

Notes

  • Implemented as a single enum carrying the altitude discipline in doc comments (module doc, per-variant docs, and a one_enum_both_altitudes test asserting the 4-member trait-shape vs 5-member facade-shape split) — the reading recommended by the task's named design point. Payloads verbatim: only Io(String) is annotated with a payload in store-api.md/ADR-012 §6.5; Missing, Verification, GcRefuse, KeyInvalid are the unit variants the spec text itself writes, so no payloads were invented for them.
  • #[non_exhaustive] not applied per the task's ADR-pinned variant set. Error derives Debug/PartialEq/Eq (test/ctor utility; no behavior added).
  • Re-exported as pub use error::Error from lib.rs; module remains public per the scaffold shape.
  • The missing_is_exclusive_to_err_shaped_ops test is the marker for the get/stat-exclusion doc contract; behavioral enforcement is deferred to the wave-2 facade task, as scoped.

Summary

src/error.rs now defines the crate's error family: one thiserror-derived Error enum with exactly the five distinguished variants (Missing, Verification, Io(String), GcRefuse, KeyInvalid), doc comments traceable to store-api.md §Error model and ADR-012 §6.5/§1 stating each variant's semantics plus the one-enum/ two-altitude discipline (trait boundary: Missing/Verification/Io/ KeyInvalid effective; KeyInvalid never fires at the boundary, GcRefuse never fires below the facade — the facade re-exposes the family with GcRefuse added). Re-exported from lib.rs. Three unit tests cover variant construction, the single-enum altitude shape, and the Missing-is-never-get/stat-absence doc contract. Verified: cargo test, cargo clippy --all-targets -- -D warnings, cargo fmt --check all green.