-
Notifications
You must be signed in to change notification settings - Fork 1
Description
Context
The weaver-spec defines ChoiceCard as a menu containing multiple SelectableItems — a bounded decision set presented to the LLM:
# Spec: ChoiceCard (1:N — menu of items)
@dataclass
class ChoiceCard:
id: str
items: list[SelectableItem] # multiple items per card
context_hint: Optional[str] = None
metadata: Dict[str, Any] = {}contextweaver's ChoiceCard is a compact view of a single item (1:1):
# Current: ChoiceCard (1:1 — compact view of one item)
@dataclass
class ChoiceCard:
id: str
name: str
description: str
tags: list[str] = []
kind: str = "tool"
namespace: str = ""
has_schema: bool = False
score: float | None = None
cost_hint: float = 0.0
side_effects: bool = FalseThese are semantically different types with the same name. The spec's ChoiceCard is a group/menu; contextweaver's is an item summary. This mismatch blocks spec-compliant RoutingDecision construction (#151), contract adapter round-trips (#143), and cross-repo interop.
Why it matters
RoutingDecision.choice_cards([routing] Add RoutingDecision contract type to match weaver-spec output shape #151) expects a list of menus, each containing multiple items. If contextweaver'sChoiceCardis 1:1, the structure is flat, losing the grouping that helps LLMs navigate large tool sets.- Contract adapters (Add weaver-spec contract adapters (SelectableItem / ChoiceCard / RoutingDecision / Frame) #143) cannot perform lossless round-trip mapping when the types have different semantics.
- Pillar 1 of the vision — the spec's
ChoiceCardis the simplified view: a bounded choice set presented as a menu, not a flat list of 50 individual tool summaries.
Decision Required
There are two alignment strategies:
Option A: Rename + Restructure (recommended)
- Rename current
ChoiceCard→ItemSummary(orCompactItem) — it represents a single-item compact view. - Add a new
ChoiceCardmatching the spec: a group/menu containing multipleItemSummaryitems. make_choice_cards()now groups items into menus (theChoiceGraphnodes already provide natural groupings).
Option B: Adapter-only
- Keep the internal
ChoiceCardas-is. - The contract adapter (Add weaver-spec contract adapters (SelectableItem / ChoiceCard / RoutingDecision / Frame) #143) handles the mapping: groups internal
ChoiceCards into specChoiceCardmenus. - Downside: the internal type name is misleading, and the grouping logic lives in the adapter instead of being first-class.
Acceptance Criteria
- Resolve the 1:N vs 1:1 semantic mismatch — either rename or restructure
- The type used in
RoutingDecision.choice_cards([routing] Add RoutingDecision contract type to match weaver-spec output shape #151) matches the spec'sChoiceCardshape -
render_cards_text()updated to render grouped choice menus -
item_to_card()updated or replaced to work with the new structure - Contract adapter (Add weaver-spec contract adapters (SelectableItem / ChoiceCard / RoutingDecision / Frame) #143) can perform lossless round-trip mapping
- All existing tests updated
- Breaking change documented in CHANGELOG.md
Implementation Notes
The ChoiceGraph DAG already provides natural groupings — each node's children are a bounded set of options. The TreeBuilder creates exactly these groups. The spec's ChoiceCard should map to one level of the graph traversal: "here are 5 groups of tools; pick a group, then pick within it."
Files likely touched:
src/contextweaver/envelope.py— type rename/restructuresrc/contextweaver/routing/cards.py— rendering functionssrc/contextweaver/context/manager.py— prompt buildingsrc/contextweaver/__init__.py— exports- Multiple test files
Dependencies
- Blocks [routing] Add RoutingDecision contract type to match weaver-spec output shape #151 (RoutingDecision — needs correct ChoiceCard shape)
- Blocks Add weaver-spec contract adapters (SelectableItem / ChoiceCard / RoutingDecision / Frame) #143 (contract adapters — round-trip mapping)
- Related: weaver-spec
contracts/python/src/weaver_contracts/core.py