cap: Documentation
Browse Sign In

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 p legally handle request r?

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:

$$ \text{Dispatch}(p, r) \iff (i_r = \top \lor i_r \preceq i_p) \land (o_r = \top \lor o_p \preceq o_r) \land y_r \preceq y_p $$

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)

$$ i_r \preceq i_p $$

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)

$$ o_p \preceq o_r $$

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)

$$ y_r \preceq y_p $$

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:bytes says “I accept any bytes”
  • media:model-spec $\preceq$ media:bytes is TRUE — PASS

When request has in=media:bytes:

  • Provider with in=media:model-spec says “I only accept model-spec”
  • media:bytes $\preceq$ media:model-spec is 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

$$ \forall c \in C,\; \text{Dispatch}(c, c) $$

Any capability can handle itself. Follows from reflexivity of $\preceq$ on each axis.

Transitivity

$$ \text{Dispatch}(a, b) \land \text{Dispatch}(b, c) \implies \text{Dispatch}(a, c) $$

If a can handle b’s requests, and b can handle c’s requests, then a can handle c’s requests.

NOT Symmetric

$$ \text{Dispatch}(p, r) \;\not\!\!\implies \text{Dispatch}(r, p) $$

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:

$$ \text{Dispatch}(p, r) \implies \text{Dispatch}(p', r) $$

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:

$$ \text{Dispatch}(p, r) \iff (i_r = \top \lor i_r \preceq i_p) \land (o_r = \top \lor o_p \preceq o_r) \land y_r \preceq y_p $$
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.