Skip to content

[routing] Add RoutingDecision contract type to match weaver-spec output shape #151

@dgenio

Description

@dgenio

Context

The weaver-spec defines RoutingDecision as contextweaver's primary output — the structured result of the routing phase, containing a list of ChoiceCards and optional selection metadata. contextweaver currently outputs a RouteResult dataclass that has a fundamentally different shape:

# Current: RouteResult (contextweaver)
@dataclass
class RouteResult:
    candidate_items: list[SelectableItem] = []
    candidate_ids: list[str] = []
    paths: list[list[str]] = []
    scores: list[float] = []
    debug_trace: list[dict[str, Any]] = []

# Spec: RoutingDecision (weaver-spec)
@dataclass
class RoutingDecision:
    id: str
    choice_cards: list[ChoiceCard]
    timestamp: datetime
    selected_item_id: Optional[str] = None
    selected_card_id: Optional[str] = None
    context_summary: Optional[str] = None
    metadata: Dict[str, Any] = {}

RouteResult is a flat list of scored candidates. RoutingDecision is a structured decision record with grouped ChoiceCards and selection tracking. Without a RoutingDecision type, contextweaver's output is not spec-compliant, and the contract adapter (#143) has no internal type to map from.

Why it matters

  • Blocks Add weaver-spec contract adapters (SelectableItem / ChoiceCard / RoutingDecision / Frame) #143 — the contract adapter needs an internal RoutingDecision equivalent to implement to_weaver() / from_weaver() round-trip mapping.
  • Spec invariant I-03 — "Routing presents bounded choices, not full schema catalogs." RoutingDecision embeds grouped ChoiceCards, enforcing this invariant structurally.
  • Cross-repo interop — ChainWeaver and agent-kernel both consume RoutingDecision. Without one, contextweaver can't participate in the ecosystem contract chain.

Acceptance Criteria

  • RoutingDecision dataclass in src/contextweaver/envelope.py (or types.py) with fields matching the spec contract shape:
    • id: str — unique decision identifier
    • choice_cards: list[ChoiceCard] — the bounded choices presented
    • timestamp: datetime
    • selected_item_id: str | None — which item was ultimately selected
    • selected_card_id: str | None — from which card
    • context_summary: str | None — optional rendering of the decision context
    • metadata: dict[str, Any]
  • to_dict() / from_dict() methods on RoutingDecision (per conventions)
  • Router.route() returns or can produce a RoutingDecision (in addition to or replacing RouteResult)
  • RouteResult preserved for backward compatibility (or deprecated with migration path)
  • Unit tests for construction, serialization round-trip, and integration with Router
  • Exported from __init__.py

Implementation Notes

Option A (preferred): Router.route() returns RoutingDecision which embeds the RouteResult data in a spec-compliant envelope. RouteResult becomes an internal detail or is deprecated.

Option B: Add a Router.decide() method that returns RoutingDecision, keeping route() unchanged for backward compatibility.

Files likely touched:

  • src/contextweaver/envelope.py — new RoutingDecision dataclass
  • src/contextweaver/routing/router.py — produce RoutingDecision
  • src/contextweaver/__init__.py — export
  • tests/test_envelope.py or tests/test_router.py — tests

Dependencies

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions