Case study. R / Shiny / Text-to-SQL.
Horizon CAPA Dashboard
Text-to-SQL quality management, fiscal-year KPIs and semantic deviation search
Internal enterprise application, built at Takeda Pharmaceutical
- Role
- Full-stack: app, text-to-SQL layer, tool-calling design, DevSight merge
- Status
- Proof of conceptv1.2.0not in production
- Context
- Takeda LA
- Tokens per prompt
- ~500
- Tabs
- 5
- Chunks in the inherited DevSight index
- 31,577
- Fiscal-year KPIs tracked
- 4
On-demand tool calling, versus 8–12K monolithic
Dashboard, Calendar, KPIs, AI Insights, Search
2,213 records, 2022 to 2026
Fiscal-year posture, plant-wide

The problem
Quality teams needed answers from CAPA, CAPA-task and deviation data but depended on analysts to write SQL and build reports. They wanted to prioritize open items by real precedent rather than generic benchmarks, see the fiscal-year compliance posture without assembling a deck by hand, and — after the standalone search tool was decommissioned — keep semantic search over four years of deviation records somewhere they could still open.
Architecture
- 1.Sources
- CAPA · task · deviation extracts
- Deviation vector index
- 2.Unified table
- RSQLite in-memory
- Precomputed due status
- Days open
- 3.Ask
- Text-to-SQL
- Manual filters
- Semantic search
- 4.R computes
- FY KPIs · closure stats
- Similarity ranking
- Insight cards
- 5.Advisor
- 5 tools, called on demand
- ~500-token prompts
- Cites matched records
- 6.Example answer
“Three closed CAPAs with similar wording took longer to close than this item has been open; the closest match is cited. Ranked by their actual closure time, not a benchmark.”
Based on
- Similar closed records, retrieved
- Actual closure time per match
- Task breakdown per match
- Tool results, quoted not derived
How it is kept honest
A hardened prompt: quote only, no arithmetic, never invent an identifier, label truncated lists as truncated. KPIs, closure statistics and insight cards are computed in R; the advisor calls five local R tools on demand and cites the matched record.
- Guardrails
- Tool bodies are local R and the call stays a single round-trip
- No SQL date functions: due-status and days-open are precomputed columns
- A 60-second streaming watchdog, 3-second rate limiting and 10 turns of memory
- Streamed synthesis quotes rather than derives
- How correctness is checked
- The inherited Deviation Search ships as pure R utilities plus one Shiny module with its own test suite. The roughly 30-minutes-to-2 search-time figure was measured on DevSight in production; its effect inside Horizon has not been measured, so the proof of concept states the value qualitatively.
- Cost
- About 500 tokens per prompt with on-demand tool calling, versus 8–12K for a monolithic prompt that front-loads a schema and a rulebook.
- Where it can still be wrong
- Proof of concept, not in production: no production usage data exists yet, and no new number is quoted until it does
- Text-to-SQL can misread a question; the sidebar's manual filters give the same result path without the model
Reliability and scale
- Unified CAPA, task and deviation table in RSQLite in memory, because DuckDB cannot be installed on the hosting server
- Month arithmetic hand-rolled because lubridate cannot be installed; a helper undoes SQLite's numeric-date coercion
- Activity logging re-uploads the whole session file per event to avoid a read-modify-write race; a logging failure never interrupts the user
What it does
Horizon is a text-to-SQL quality dashboard for quality managers and directors, built on the querychat package with Databricks-hosted models. A dual-mode sidebar takes either a plain-English question (translated to SQL over a unified CAPA / task / deviation table) or manual filters, and every tab respects the result.
- Dashboard — Overdue / Due 7 / Due 30 / On Track value boxes with CAPA-vs-task sub-counts and click-through drill-downs, an 18-month trend with cumulative backlog, and closure-distribution boxplots
- Calendar — a monthly view in a 9-color scheme: 3 record types × 3 urgency levels
- Quality KPIs — the fiscal-year posture, plant-wide
- AI Insights — a streaming advisor that calls tools on demand
- Deviation Search — the semantic search inherited from DevSight
Historical-similarity prioritization
The standout feature: the Prioritize action uses LLM tool calling to find semantically similar closed CAPAs, pull each match's actual closure time and task breakdown, and rank open items against that real history rather than an aggregate benchmark — citing the matched record. Calling tools on demand keeps prompts at ~500 tokens versus 8–12K for a monolithic prompt that front-loads a schema and a rulebook; the tool bodies are local R, so it stays a single round-trip.
- Five registered ellmer tools and five quick-analysis actions: full analysis, risk report, bottlenecks, by department, prioritize
- Owner-workload chart, a 60-second streaming watchdog, 3-second rate limiting, and 10 turns of memory
- A hardened prompt: quote only, no arithmetic, never invent an identifier, label truncated lists as truncated
Fiscal-year KPIs and a board-ready one-pager
The Quality KPIs tab is written for the people who present, not the people who query. A headline posture strip sits above four fiscal-year KPIs as red / amber / green value boxes with month-over-month delta arrows, 2×2 small-multiple trend panels with target lines, on-time CAPA closure by department, a clickable scorecard, a risk watchlist (top overdue, due in the next 7 days, aging, overdue by owner) and a KPI methodology modal. One click prints a board-ready one-pager.
Deviation Search — the DevSight successor
When DevSight was decommissioned, its general-purpose search moved here on 2026-08-14 as pure R utilities plus one Shiny module with its own test suite. It searches four years of deviation records — 2,213 records indexed as 31,577 chunks — with natural-language date filtering, streamed synthesis that quotes rather than derives, and R-computed insight cards: closure statistics, root-cause themes, similar past records.
One discipline carried over intact. Search time was measured on DevSight, in production, at roughly 30 minutes down to 2. Its effect inside Horizon has not been measured, so the proof of concept states the value qualitatively and refuses to quote a new number until production data supports one.
Constraint-driven engineering
- DuckDB can't be installed on the hosting server, so the unified table lives in RSQLite in-memory with precomputed due-status and days-open columns and a hard 'no SQL date functions' rule in the prompt
- lubridate can't be installed either — month arithmetic is hand-rolled, with a helper that undoes SQLite's numeric-date coercion
- Activity logging accumulates rows in memory and re-uploads the whole session file per event, deliberately avoiding a read-modify-write race; a logging failure never interrupts the user
Decommissioned, then rebuilt as a proof of concept
Horizon was decommissioned in July 2026 during a global application consolidation — while it was performing well, weeks after v1.1.0 shipped with a roadmap through 2027. In August it was rebuilt as v1.2.0 because the site still needed a home for DevSight's search, and Horizon was the surface that could carry it. It remains a proof of concept, not in production. Build the hard part once; carry it forward.
Honest state of play: the merged search module is tested; the fiscal-year KPI helpers that go to management review are not yet, and that is the next item on the list.
Stack
- R Shiny
- querychat
- ellmer
- Databricks Foundation Models
- Databricks Vector Search
- RSQLite
- AWS S3