Dispatch
The dispatch predicate, three-axis variance, and routing validity
The Central Question
Given:
- A provider
p(a registered capability) - A request
r(what the caller wants)
The dispatch question is:
Can provider
plegally handle requestr?
This is answered by the dispatch predicate — the primary routing predicate in capdag.
The Dispatch Predicate
Definition
Let:
- $p = (i_p, o_p, y_p)$ — provider
- $r = (i_r, o_r, y_r)$ — request
Then:
Where $\top$ = media: (the identity/top of the media partial order). A request dimension set to $\top$ is unconstrained — the axis is vacuously true.
Provider wildcards need no special case. $i_p = \top$ passes because $\forall x,\; x \preceq \top$. $o_p = \top$ correctly fails for specific $o_r$ because $\top \preceq o_r$ is false (top does not conform to a more specific type).
The Three Conjuncts
| Axis | Condition | Variance | Meaning |
|---|---|---|---|
| Input | $i_r = \top \lor i_r \preceq i_p$ | Contravariant | Request unconstrained, or input conforms to provider |
| Output | $o_r = \top \lor o_p \preceq o_r$ | Covariant | Request unconstrained, or provider output conforms |
| Cap-tags | $y_r \preceq y_p$ | Invariant/Refinement | Provider satisfies request’s constraints |
Variance Interpretation
Input Axis (Contravariant)
The provider may accept MORE input types than the request specifies. Function parameter types are contravariant.
Request: in="media:pdf;bytes" (specific)
Provider: in="media:bytes" (more general)
i_r = media:pdf;bytes
i_p = media:bytes
i_r ⪯ i_p? → Does pdf;bytes conform to bytes?
→ Yes, pdf;bytes is more specific than bytes
→ PASS ✓
A provider accepting media:bytes can handle a request sending media:pdf;bytes.
Output Axis (Covariant)
The provider must produce AT LEAST as specific output as the request requires. Function return types are covariant.
Request: out="media:object" (general requirement)
Provider: out="media:object;textable" (more specific guarantee)
o_p = media:object;textable
o_r = media:object
o_p ⪯ o_r? → Does object;textable conform to object?
→ Yes, object;textable is more specific than object
→ PASS ✓
A provider guaranteeing media:object;textable satisfies a request needing media:object.
Cap-Tags Axis (Invariant + Refinement)
The provider must satisfy all explicit request constraints and may refine omitted ones.
Request: op=extract (requires extract operation)
Provider: op=extract;target=metadata (provides extract with refinement)
y_r = {op: "extract"}
y_p = {op: "extract", target: "metadata"}
y_r ⪯ y_p? → Request has op=extract, provider has op=extract → match
→ Request omits target, provider has target=metadata → OK (refinement)
→ PASS ✓
Dispatch Is NOT Symmetric
Dispatch(p, r) does NOT imply Dispatch(r, p).
The input condition $i_r \preceq i_p$ means:
- Request’s input must be at least as specific as provider’s input
- Provider’s accepted input must subsume request’s input
When request has in=media:model-spec:
- Provider with
in=media:bytessays “I accept any bytes” media:model-spec$\preceq$media:bytesis TRUE — PASS
When request has in=media:bytes:
- Provider with
in=media:model-specsays “I only accept model-spec” media:bytes$\preceq$media:model-specis FALSE — FAIL
Wildcard Handling
media: is the identity (top of the partial order). As a dimension value in dispatch, it means “unconstrained” — the axis imposes no restriction and is vacuously true.
| Request Input | Provider Input | Dispatch? | Reason |
|---|---|---|---|
media: |
media: |
✓ | Both unconstrained |
media: |
media:pdf |
✓ | Request unconstrained |
media:pdf |
media: |
✓ | Provider accepts any |
media:pdf |
media:bytes |
✓ | pdf conforms to bytes |
media:pdf |
media:image |
✗ | pdf does not conform to image |
Axis-by-Axis Rules
Input Axis
| Request In | Provider In | Dispatchable? | Reason |
|---|---|---|---|
media: (any) |
any | ✓ | Request unconstrained |
| specific | media: (any) |
✓ | Provider accepts any |
| specific | same | ✓ | Exact match |
| more specific | less specific | ✓ | Provider accepts broader class |
| less specific | more specific | ✗ | Request might send unsupported |
| incomparable | incomparable | ✗ | Different type families |
Output Axis
| Provider Out | Request Out | Dispatchable? | Reason |
|---|---|---|---|
| any | media: (any) |
✓ | Request unconstrained |
media: (any) |
specific | ✗ | Provider can’t guarantee required |
| same | same | ✓ | Exact match |
| more specific | less specific | ✓ | Provider exceeds requirement |
| less specific | more specific | ✗ | Provider may not meet requirement |
| incomparable | incomparable | ✗ | Different type families |
Cap-Tags Axis
| Request Tag | Provider Tag | Dispatchable? | Reason |
|---|---|---|---|
| missing | missing | ✓ | No constraint |
| missing | K=v | ✓ | Provider refines |
| K=v | K=v | ✓ | Exact match |
| K=v | K=w (w $\neq$ v) | ✗ | Contradiction |
| K=v | missing | ✗ | Provider lacks required |
| K=* | K=v | ✓ | Provider has a value |
| K=* | missing | ✗ | Provider lacks required |
Examples
Generic Request, Specific Provider
Request: cap:op=download-model
Provider: cap:in="media:model-spec";op=download-model;out="media:download-result"
Input: i_r=media: (⊤), i_p=media:model-spec
Request unconstrained → PASS ✓
Output: o_p=media:download-result, o_r=media: (⊤)
Request unconstrained → PASS ✓
Tags: y_r={op:download-model}, y_p={op:download-model}
Provider has required op → PASS ✓
Result: DISPATCHABLE ✓
Specific Request, Generic Provider (Fallback Failure)
Request: cap:in="media:pdf";op=extract;out="media:object"
Provider: cap:in="media:bytes";op=extract;out="media:"
Input: i_r=media:pdf, i_p=media:bytes
pdf ⪯ bytes? Yes → PASS ✓
Output: o_p=media:, o_r=media:object
media: ⪯ media:object? No, identity is NOT more specific
→ FAIL ✗
Result: NOT DISPATCHABLE
Incompatible Types
Request: cap:in="media:pdf";op=convert;out="media:html"
Provider: cap:in="media:image";op=convert;out="media:text"
Input: i_r=media:pdf, i_p=media:image
pdf ⪯ image? No, different families → FAIL ✗
Result: NOT DISPATCHABLE (fails at first axis)
Implementation
Method Signature
impl CapUrn {
pub fn is_dispatchable(&self, request: &CapUrn) -> bool;
}
Usage:
if provider.is_dispatchable(&request) {
// provider can handle request
}
Pseudocode
fn is_dispatchable(&self, request: &CapUrn) -> bool {
// Input axis (contravariant)
// media: is unconstrained — vacuously true on either side
if request.in_urn != "media:" && self.in_urn != "media:" {
let req_in = MediaUrn::from_string(&request.in_urn);
let prov_in = MediaUrn::from_string(&self.in_urn);
if !req_in.conforms_to(&prov_in) {
return false;
}
}
// Output axis (covariant)
// Request media: = unconstrained (accept anything) → pass
// Provider media: = no guarantee → fail when request is specific
if request.out_urn == "media:" {
// Request unconstrained — pass
} else if self.out_urn == "media:" {
return false; // Provider can't guarantee specific output
} else {
let prov_out = MediaUrn::from_string(&self.out_urn);
let req_out = MediaUrn::from_string(&request.out_urn);
if !prov_out.conforms_to(&req_out) {
return false;
}
}
// Cap-tags axis: provider must satisfy request constraints
if !self.cap_tags_dispatchable(request) {
return false;
}
true
}
Properties
Reflexivity
Any capability can handle itself. Follows from reflexivity of $\preceq$ on each axis.
Transitivity
If a can handle b’s requests, and b can handle c’s requests, then a can handle c’s requests.
NOT Symmetric
A specific provider can dispatch a generic request, but not vice versa.
Monotonicity
If provider $p'$ refines $p$ (same or more general input, same or more specific output, same or more specific y-tags), then:
Refinement preserves dispatchability.
Common Mistakes
Using accepts for Dispatch
// WRONG:
if provider.accepts(&request) { /* dispatch */ }
This ignores the mixed-variance nature of Cap URNs.
Using conforms_to for Dispatch
// WRONG:
if provider.conforms_to(&request) { /* dispatch */ }
This also ignores mixed variance.
Checking Only One Axis
// WRONG:
if provider.op == request.op { /* dispatch */ }
All three axes must be checked.
Summary
The dispatch predicate is:
| Property | Value |
|---|---|
| Input variance | Contravariant |
| Output variance | Covariant |
| Cap-tags variance | Invariant + Refinement |
| Symmetric? | NO |
| Reflexive? | YES |
| Transitive? | YES |
This is the primary predicate for routing. Ranking applies only after dispatch validity is established.