ADR-0028: Claim Check Repository Trait Boundary
Date: 2026-06-27 Status: Amended by rc-7qf (originally Accepted, Phase 2) References: ADR-0022, ADR-0023, rc-7qf
Decision
Separate ClaimCheckRepository trait (not on IdempotentRepository)
Claim Check EIP stashes large message payloads by key so the Exchange carries only a lightweight reference. This is a payload-bearing pattern (set/get with Message values), structurally distinct from the Idempotent Consumer (key-only contains/add). Each pattern owns its own trait; the shared NamedRegistry<T> wiring pattern is cross-referenced but not inherited.
File: crates/camel-api/src/claim_check.rs:16-52
#[async_trait]
pub trait ClaimCheckRepository: Send + Sync + std::fmt::Debug + 'static {
fn name(&self) -> &str;
async fn set(&self, key: &str, payload: Message) -> Result<(), CamelError>;
async fn get(&self, key: &str) -> Result<Message, CamelError>;
async fn get_and_remove(&self, key: &str) -> Result<Message, CamelError>;
async fn remove(&self, key: &str) -> Result<(), CamelError>;
async fn push(&self, key: &str, payload: Message) -> Result<(), CamelError>;
async fn pop(&self, key: &str) -> Result<Message, CamelError>;
}
Payload type: Message (body + headers)
The trait stores camel_api::Message (body + headers) instead of Body alone. This preserves headers across Claim Check operations — a Set stashes the full exchange.input Message, and a Get can selectively merge back headers via the filter option. Callers who only care about the body access stashed.body.
File: crates/camel-api/src/claim_check.rs:13 (import), crates/camel-api/src/message.rs:8 (Message struct).
Stack operations: push / pop
The trait includes LIFO stack operations (push, pop) so Claim Check can be used with the Push/Pop EIP variant without a second trait. The stack is key-scoped (push("stack-key", body)). Separate from the single-value key space (set("key", body)).
File: crates/camel-api/src/claim_check.rs:47-52.
Contract: get_and_remove is atomic
get_and_remove returns and deletes in one atomic step. This mirrors the Claim Check's "claim once" semantics — after checkout the payload is released. Implementations MUST ensure no concurrent reader can observe the payload after get_and_remove returns.
File: crates/camel-api/src/claim_check.rs:39-43.
remove is idempotent
remove succeeds even if the key does not exist (no-op). Matches IdempotentRepository::remove contract.
File: crates/camel-api/src/claim_check.rs:45, crates/camel-api/src/idempotent.rs:36.
Memory implementation: DashMap + VecDeque
MemoryClaimCheckRepository uses DashMap<String, Body> for single-value keys and DashMap<String, VecDeque<Body>> for LIFO stacks. Interior mutability allows &self for all trait methods, safe for concurrent Arc<dyn ClaimCheckRepository> sharing.
File: crates/camel-core/src/claim_check/memory_repository.rs:17-20.
Registration: NamedRegistry<T> reuse
The Claim Check registry reuses Phase 1's NamedRegistry<T> (Mutex-based, duplicate-detecting) with ClaimCheckRegistry and SharedClaimCheckRegistry type aliases. Pattern identical to IdempotentRegistry/SharedIdempotentRegistry.
File: crates/camel-core/src/registry.rs:90-98.
CamelContext wiring
CamelContext exposes register_claim_check_repository() and claim_check_repository() methods, mirroring the idempotent repository API. The builder registers a default "memory" repository.
File: crates/camel-core/src/context.rs:700-719, crates/camel-core/src/context_builder.rs:248-256.
Lifetime threading
CompilationContext carries claim_check_repositories: &'a ClaimCheckRegistry for future step compilers. No step compiler uses it yet (warning suppressed); the field exists for the upcoming Claim Check DSL step compiler.
File: crates/camel-core/src/lifecycle/adapters/step_compilers/mod.rs:105-106.
Does NOT implement StepLifecycle
Like MemoryIdempotentRepository, MemoryClaimCheckRepository holds only Arc-shared DashMaps with no background work (timers, buckets, queues). No StepLifecycle implementation needed. If a persistent backend (Redis, SQL) requires connection lifecycle management, it should implement StepLifecycle on the backend client, not the repository wrapper.
File: crates/camel-api/src/step_lifecycle.rs:31 (StepLifecycle trait).
Body is Clone, stored directly
Body implements Clone (file: crates/camel-api/src/body.rs:178-189). The memory repository stores Body values directly (not Arc<Body>). This is safe because Body variants clone cheaply: Bytes (Arc-backed copy-on-write), Text/Xml (String clone), Json (serde_json::Value clone), Stream (Arc<Mutex<Option<...>>> clone — shared stream handle).
Filter for selective merge-back (rc-7qf)
Phase 2 (rc-blw) implemented full-Message store/retrieve. Phase 2b (rc-7qf) adds a filter option on Get/GetAndRemove/Pop that controls which parts of the stashed Message are merged back into the current exchange:
body: Include (restore), Exclude (keep current), Remove (clear body)headers: Include all, Exclude all, Remove all, or by pattern (include/exclude/remove matching header keys)- Patterns support exact match,
*prefix wildcard, and regex attachmentskeyword is parsed as no-op (no attachment support in Phase 2b)
The filter is parsed at route-compile time into ClaimCheckFilter (camel-processor). ClaimCheckService::call() applies it via the merge_stashed() helper. No-filter = backward-compatible body-only restore.
CamelError::RouteError for "not found"
The memory repository returns CamelError::RouteError(format!("...")) for missing-key lookups and empty-stack pops. No new CamelError variant is introduced.
File: crates/camel-core/src/claim_check/memory_repository.rs:55-56, crates/camel-core/src/claim_check/memory_repository.rs:88.
References
- ADR-0022: StepLifecycle trait and drain —
docs/adr/0022-steplifecycle-trait-and-drain.md - ADR-0023: Idempotent Repository trait (structural parent) —
docs/adr/0023-idempotent-repository-trait.md - ADR-0024: PipelineOutcome replaces CamelError::Stopped —
docs/adr/0024-pipeline-outcome-replaces-camel-error-stopped.md NamedRegistry<T>:crates/camel-core/src/registry.rs:33-34IdempotentRegistryalias:crates/camel-core/src/registry.rs:77-78Bodyenum:crates/camel-api/src/body.rs:162-176- ClaimCheck EIP spec: Enterprise Integration Patterns (Gregor Hohpe, 2004), Chapter 10