5.6 KiB
id, name, status, depends_on, scope, risk, impact, level, tags
| id | name | status | depends_on | scope | risk | impact | level | tags | |||
|---|---|---|---|---|---|---|---|---|---|---|---|
| core-errors-and-validation | Core error taxonomy and name validation | completed |
|
narrow | low | project | implementation |
|
Description
Implement the core crate's error taxonomy and the name-validation helpers every entry point shares.
Error taxonomy (ADR-008 §5, exact): one top-level Error,
thiserror-typed, #[non_exhaustive] (ADR-017 §3 — mechanically
enforces catch-all matching). Variants:
PayloadTooLarge { limit }— universal variant; produced pg-side only (the occurrence asymmetry is engine work, not core's — core defines the variant and its doc text)ReservedName { name }InvalidName { name }ClosedCodecDatabase— the opaque fallback; engine detail rides the source chain (#[source]), no engine-specific variants ever minted
Name validation (ADR-008 §4): a shared validation helper applied at
every name-bearing entry point — non-empty (InvalidName) and
reserved-prefix rejection for the __alkstore_ prefix
(ReservedName). The helper distinguishes the two name classes:
- Shared-namespace kinds (channels, streams, queues, outboxes, locks, schedule names): non-empty + reserved-prefix rejected.
- Consumer-local identifier classes (stream-consumer names,
ownerontry_lock,worker_idon claims): non-empty only — a leading__alkstore_is legal (ADR-008 §4 third-round annotation).
Also the reserved-string constant: __alkstore_listener_reconnected__
(the one v1-reserved string, ADR-008 §4) — public in core so the pg
engine and the contract suite reference the same constant.
Result<T> alias in the crate's prelude shape. No panics; no
unwrap()/expect().
Acceptance Criteria
Errorwith exactly the six v1 variants,#[non_exhaustive],thiserror-derived,Databasecarrying#[source]- Validation helper(s) covering both name classes, unit-tested (empty, whitespace-only, reserved-prefix, reserved string itself, legal names)
- Reserved-string constant exported
cargo test -p alkstore, clippy-D warnings, fmt clean
References
- docs/architecture/decisions/008-contract-v1-pinning.md §4, §5
- docs/architecture/decisions/017-contract-versioning.md §3
- docs/architecture/core-contract.md §Errors, §Naming / reserved namespace
Notes
thiserrorv2 added toalkstore— the crate's first (and, until wave 2's serde work, only) dependency.Error::Databasecarries#[source] Box<dyn std::error::Error + Send + Sync>— the opaque engine-detail carrier per ADR-008 §5. The#[error(...)]text is deliberately terse ("database error") so it doesn't shadow the source chain's detail. AnError::database(err)convenience constructor wraps anythingInto<Box<dyn Error + Send + Sync>>.Error::PayloadTooLarge { limit: usize }— the limit isusize(a byte count, not a std::io::ErrorKind-style re-export); doc text carries the ADR-020 §4 serialization-quantity pin and the pg-only/never-SQLite asymmetry.- Validation helpers split by name class exactly per ADR-008 §4's
third-round annotation:
validate_shared_name(six shared kinds — non-empty + reserved-prefix rejection) andvalidate_local_name(stream-consumer names,try_lockowner,worker_id— non-empty only).NameKind::Shared/NameKind::Local+validate_name(kind, name)is the underlying pair; the two named helpers are the ergonomic surface. Whitespace-only names are rejected asInvalidNamefor both kinds (never a meaningful name on any engine); names are not trimmed (the caller's name is stored as given — engine quoting is the engine's obligation). - The reserved-string test asserts its shared-name rejection carries
ReservedName(it carries the reserved prefix, so it can never be a legal shared name) whilevalidate_local_nameaccepts it — the two name classes distinguished in tests, mirroring the ADR text. #[non_exhaustive]was verified on the enum definition, not in this crate's tests: in-crate matching stays exhaustive (the mechanical catch-all enforcement binds downstream crates, per the Rust rules ADR-017 §3 relies on) — a test here asserting a necessary wildcard is unreachable-code, flagged clippywarn(unreachable_patterns).schedule()/run_schedules'InvalidSpec/LeadershipLostvariants (core-contract §Errors, ADR-009 §6) are not minted here — the six-variant list is this task's exact scope; the scheduler variants land with the trait-surface task that pins their signatures.
Summary
Core error taxonomy and name validation implemented in alkstore.
src/error.rs: Error — exactly the six v1 variants
(PayloadTooLarge { limit }, ReservedName { name },
InvalidName { name }, Closed, Codec(String), Database
with #[source]), thiserror-derived, #[non_exhaustive], plus
the Result<T> alias (the prelude shape downstream crates use).
src/validation.rs: RESERVED_PREFIX = "__alkstore_",
RESERVED_LISTENER_RECONNECTED = "__alkstore_listener_reconnected__" (public for the pg engine and
contract suite), NameKind::{Shared, Local}, and
validate_shared_name/validate_local_name/validate_name
helpers. All types re-exported from src/lib.rs. Ten unit tests
cover empty/whitespace-only/reserved-prefix/reserved-string/legal
names for both classes and the error display/source behavior.
cargo test -p alkstore (10 pass), cargo clippy --all-targets -- -D warnings, and cargo fmt --check all clean;
workspace cargo build compiles all three crates.