ADR-005: Widget Builder via LangGraph Clarifier¶
Status: Accepted Source: prd.md §19
Context¶
PRD §11.1 originally listed Add Widget as a no-op visual stub. The pitch (prd.md §20) leans heavily on the pattern reusability story — "the same Event → Context → AI → Action → Feedback architecture extends to fraud, inventory, retention, product quality." A chat-driven widget builder makes that pattern tangible during the demo: the executive creates a new dashboard slice in 30 seconds without engineering involvement, and the same Spine-Clarifier pattern that ships in sdlc-agent-swarms/packages/agents-clarifier (LangGraph StateGraph + interrupt_before HITL + SSE + typed Zod artifacts) is what powers it.
Decision¶
- The
Add Widgetbutton opens a split-panel modal. The left pane runs a LangGraph clarifier with a Pydantic-discriminatedWidgetSpecunion (kpi|chart|table). The right pane renders a live preview of the spec. - The clarifier uses the same node names and topology as the Spine Clarifier:
contextLoader → intentExtractor → gapDetector → questionPrioritizer → specSynthesizer → critic → specUpdater, withinterrupt_before=['specSynthesizer']so the user answers questions between rounds. Resume viagraph.update_state({"human_responses": [...]})thengraph.invoke(None, config). - The user-facing terminal state is
preview, notcomplete— we explicitly require an Add to dashboard click before the widget is persisted into the newwidgetsPostgres table. This matches the chosen "preview-then-confirm" UX (see plan §"Widget creation scope decisions"). - Bedrock is the LLM backend (matching ADR-002 production target), called via
boto3bedrock-runtimewith JSON mode via tool-use so the spec is structurally validated by the discriminator and not by free-text parsing. A deterministicMockLlm(no network) is used whenuse_bedrock=Falseor the call times out atwidget_llm_timeout_s(default 4s) — same fallback shape as ADR-003. Note: the silent-substitution semantics this point introduced were superseded by ADR-008. Inlivemode, Bedrock failures now surface asBuilderModeError;MockLlmis reserved for explicitBUILDER_MODE=offline. - No RAG. The widget catalog (renderer types, accents, Recharts chart kinds) and design tokens are injected inline by
contextLoader. The "evolution mode" RAG path of the Spine Clarifier (Voyage + Qdrant + Cohere rerank) is explicitly out of scope for v1. - Live data binding is a separate user story. Each
WidgetSpeccarries adata_intentblock (entity,metric,dimensions,filters,refresh_seconds) that the next story will resolve into a real query against the event stream. Today the preview renders the LLM-generatedmock_datablock. (Part C is the slice that proves data binding for 2–3 metrics; see ADR-PROTO-005.)
Consequences¶
- Header button changes from no-op to wired entry point. PRD §3.2 ("Non-Goals") loses the
Add Widget no-opline; §5.1 gains row 15 marking the feature P1. - Adds two backend dependencies:
langgraphandboto3.langchain-coreis pulled transitively; we deliberately do not addlangchain-awsto keep the dep tree small (call Bedrock directly viaboto3.client("bedrock-runtime")). - New Postgres table
widgets(db/init.sql);reset_demo()truncates it somake demo-resetclears chat-built widgets. - New SSE contract documented in docs/widget-builder.md; event names match the Spine Clarifier (
stage|gaps|questions|result|error). - During an extended outage of Bedrock, the demo still runs against
MockLlminBUILDER_MODE=offline— questions and the final spec are deterministic and obviously canned, but the UX flow is identical. Per ADR-008, live-mode demos do NOT silently substituteMockLlm.
Cross-references¶
- ADR-006 — adds the
customvariant. - ADR-007 — adds the metric-aware Clarifier and first-class metric catalog.
- ADR-008 — supersedes the silent-fallback semantics introduced here.
- docs/widget-builder.md — authoritative for the Clarifier subsystem.
- Implementation: backend/app/widgets/, frontend/src/widgets/.