Overview
The problem. Operations leaders run on KPIs such as claim turnaround or invoice-to-pay time. When a KPI slips, finding the cause takes analysts days of slicing data. A language model asked "why" will produce a fluent answer with invented numbers and plausible but wrong causes.
The approach. WatchDog keeps the model for what it is good at (forming hypotheses, reading evidence, writing) and leaves every calculation to code.
Who it is for. Operations and process-excellence leaders, and the consultants who serve them.
Why the results can be trusted.
- Synthetic data with planted root causes and decoys, so "did it find the real causes, and did it fall for the decoys?" is a measured number.
- Two different processes (insurance claims, procure-to-pay) on one engine, which shows the engine is horizontal.
- Governance in the backend: agents can propose and execute, never approve; every step lands in a tamper-evident audit log.
- Zero spend: free model tiers, local embeddings, local-first deployment.
Live demo
WatchDog runs publicly at intelligence-watchdog.duckdns.org, live since 12 November 2026, on a free Oracle Cloud server and Groq's free model tier.
- Browse without signing in. Visitors have read-only access (D58).
- Replay recorded investigations. Showcase runs are real investigations recorded ahead of time. Replaying one costs no model tokens.
- Run new investigations. Sign in with GitHub. Only accounts an admin created can sign in (D56, D61).
Start with a replay. Fresh investigations draw on a free quota of about ten a day for everyone, so replays are the reliable way to see the system.
How it works
WatchDog closes the loop from a KPI alert to a verified fix. The investigation stage is where it differs from a dashboard or a chatbot.
- Watch. Monitors are configuration, with thresholds taken from the client's own policies ("critical if above 24 hours for two consecutive months"). New critical alerts are ranked and the top one is investigated automatically, within a daily budget.
- Investigate. A LangGraph workflow. Code scans the data for every significant pattern. The model turns patterns into hypotheses and picks checks from a fixed menu. Code runs every check, including confounder controls. The model judges each hypothesis, and rule overrides correct it in both directions. The executive summary's numbers are verified against the computed facts.
- Recommend. Each corrective action in the catalogue names the policy clause that permits it and a lever. A deterministic what-if on the event log values it (breaches avoided, illustrative annual ROI).
- Act. Propose, then approval by a person with the right business role, then execution through the Operations MCP server against the system of record. Agents can never approve, and the proposer cannot approve their own request.
Around the loop: the platform an enterprise would ask for
Access control
Two layers: a platform role (viewer, analyst, admin) and a business role per process.
Observability
Every LLM call and API request: latency, tokens, cache hits, free-tier quota per model, traces per investigation.
Cost control
Answer cache, replay of identical investigations, showcase runs anyone can watch, a light model tier, rate limits only on requests that spend tokens.
Three MCP servers
Analytics and Knowledge (read-only) and Operations (governed actions), so external agents get the app's numbers, not estimates.
Exploratory SQL
Read-only and sandboxed, labelled "not a verified metric".
Policy Q&A
Answers with citations, re-checked by a cross-encoder.
Two processes, one engine
Both processes run on the same engine, each defined by a "domain pack" of configuration: 18,000 cases per pack over 18 months (April 2025 to September 2026), five planted root causes, and decoys that look like causes but are explained by something else. Both companies are fictional.
Insurance claims · Kestrel General Insurance
Motor and health reimbursement claims, a five-day turnaround SLA, 20 policy and SOP documents, and a post-closure customer survey. Breach rate 26%.
| ID | Planted root cause | How it shows in the data |
|---|---|---|
| RC1 | Senior-approval queue understaffed since the approval threshold dropped from ₹5L to ₹2L on 1 April 2026 | Wait before Senior Approval steps up from about 15h to 70h and keeps rising |
| RC2 | Broker claims arrive without complete documents | Additional-documents loop on about 24% of broker claims vs 6% |
| RC3 | Team B's verification work is rejected by QA more often | Rework 17.5% for Team B vs 4.8% |
| RC4 | Work handed over late on Friday waits for the Monday backlog | Friday-afternoon handoffs wait about 53h |
| RC5 | Monsoon surveyor backlog: motor claims rise 60% in July to September and the surge panel was never activated | Survey wait triples in both monsoons (about 12h to 39h) |
| ID | Decoy (looks like) | Actually |
|---|---|---|
| DC1 | Health claims are slower | Health claims are higher-value, so they hit the senior queue (RC1) |
| DC2 | Pune underperforms | Tiny and broker-heavy (RC2) |
| DC3 | Customers are broadly unhappy | Unhappy customers answer surveys more often |
| DC4 | Kolkata claims need more rework | Four in five Kolkata claims go to Team B (RC3) |
Procure-to-pay · Varaha Auto Components
Supplier invoices at a five-plant manufacturer, a ten-day invoice-to-pay target, 13 documents. Breach rate 34%.
| ID | Planted root cause | How it shows in the data |
|---|---|---|
| RC1 | Chakan's stores post goods receipts late (one clerk short) | Goods-receipt-pending rate 23% at Chakan vs 6% |
| RC2 | Free-text purchase orders have no agreed price | Price exceptions on about 54% of free-text invoices vs 5% |
| RC3 | The outsourced BPO Partner makes capture errors | Re-capture loop on 40% of BPO invoices vs 4% |
| RC4 | Treasury cut payment runs from twice to once a week from 1 July 2026 | Wait before Payment Run rises from about 54h to 80h |
| RC5 | Paper invoices go by weekly courier to central scanning | Paper invoices breach 61% vs 27% |
| ID | Decoy (looks like) | Actually |
|---|---|---|
| DC1 | IT and professional services are slow | They are mostly free-text POs (RC2) |
| DC2 | MSME suppliers are paid late | Their invoices mostly go to the BPO Partner (RC3) |
| DC3 | The Sanand plant pays late | Three in five Sanand invoices are on paper (RC5) |
The data also carries realistic seasonality (monsoon motor surge, March fiscal year-end rush) and change points that match the dated policy documents. Every case draws its randomness up front and is re-simulated with each cause switched off, so "this breach happened because of RC1" is an exact counterfactual, not an estimate.
Results
On the six-month datasets, every completed investigation found every planted cause and no run accepted a decoy, across one local and three cloud models. On the 18-month data, the first five runs (insurance questions, seed 42) did the same.
Insurance claims · six-month data, 3 seeds × 3 questions per model
| Model | Completed | Recall of planted causes | Decoys accepted | Model's own citation accuracy |
|---|---|---|---|---|
| ollama:qwen2.5:7b (local, free) | 9/9 | 100% | 0 | 75% |
| groq:openai/gpt-oss-120b | 9/9 | 100% (79% before the D37 fix) | 0 | 73% |
| groq:openai/gpt-oss-20b | 8/9 | 100% | 0 | 69% |
| groq:qwen/qwen3.8-27b | 4/9 | 100% | 0 | 97% |
Incomplete runs were refused by Groq's free tier (quota or per-minute size cap), not model failures, and are reported separately.
Procure-to-pay (six-month data, local 7B, 9 investigations): 100% recall, 0 decoys, remedy recommended for 17 of 18 causes, policy answers grounded 100%, retrieval hit@5 100%.
Other evidence
- Policy Q&A: 96% to 100% of answers cite a relevant passage; citations are re-checked by a cross-encoder, not trusted.
- Retrieval on the larger 18-month corpus (hybrid search): the right passage is in the top five for 97% (insurance) and 100% (procure-to-pay) of benchmark questions.
- What-if estimates land at 0.5 to 0.99 of the exact counterfactual. The engine tends to understate, which is the safe direction.
- Unverified numbers in executive summaries: 0 across all completed runs.
- In one procure-to-pay evaluation the model called a confounded segment "supported" 25 times; the confounder rule refuted every one, so none reached the summary.
Limitations
- The data is synthetic and the policies author-written. The results show the system recovers planted patterns and resists planted decoys, not that it would on a messy real event log.
- Samples are small (three seeds, three to five questions per pack and model).
- Number verification checks numbers, not claims: a small model can attach a correct number to the wrong statement.
- The what-if engine understates some effects (calendar effects, mechanisms beyond waiting time).
- The full evaluation on the 18-month data, with five causes per pack, is not yet complete; the results above are mainly from the six-month data.
Architecture and stack
A modular monolith with separate processes for the watcher and three MCP servers, all on one PostgreSQL database and driven by per-process configuration. The investigation graph is the product. Everything else makes it trustworthy (governance, audit, observability) or affordable (local embeddings, free model tiers, caching). A new process is a configuration pack, not new code.
| Layer | Choice |
|---|---|
| Backend | Python, FastAPI, SQLAlchemy, Alembic (13 migrations), pytest (258 tests) |
| Agent workflow | LangGraph; structured output via JSON schema with a tool-calling fallback |
| Models | Provider-agnostic gateway: Ollama qwen2.5:7b locally, Groq free tier (gpt-oss-120b, gpt-oss-20b, qwen3.8-27b), Gemini optional |
| Retrieval | Hybrid vector + full-text with reciprocal rank fusion; BGE-small embeddings and a MiniLM cross-encoder via fastembed (ONNX, CPU) |
| Data | PostgreSQL 17 with pgvector; process mining in SQL (directly-follows graph, variants, waiting times) |
| Integration | MCP Python SDK, static bearer tokens per client, three servers |
| Frontend | React, Vite, TypeScript, Tailwind, TanStack Query |
| Runtime | Docker Compose locally and in production, with Caddy for HTTPS; CI runs lint, migrations, tests, a migration round-trip and the frontend build |
Deployment
The plan was Oracle's free 4-core ARM machine. It was out of capacity, so the demo runs on the free 1 GB AMD micro instance with 4 GB of swap (D60). The running stack uses about 0.5 GB of RAM, so responses are slower while parts of it page in from swap.
- One public entry point. Caddy gets and renews the HTTPS certificate, adds security headers, serves the built frontend and forwards
/api/*to the backend without buffering, so investigation events stream live. Postgres, the backend, the Operations MCP server and the mock enterprise API have no published ports. - Builds happen off the server. Images are built on a laptop with the same CPU architecture and streamed to the server over SSH. The first transfer took under three minutes. Building on a one-eighth-core machine would take about an hour or run out of memory.
- Data is regenerated, not copied. The generators are deterministic, so the server rebuilds the identical 18-month datasets from seed 42 and ingests the 13 policy documents.
- Replays are checked. A showcase run loads only if a fingerprint of the server's data matches the data it was recorded on, so a replay cannot quote numbers that are not there. Runs are recorded locally on the free model (D59).
- Free-tier limits. Groq allows 8,000 tokens a minute and 200,000 a day per model (D62). Calls run one at a time with up to eight retries that wait as long as the provider asks. A test investigation hit 21 per-minute refusals and still finished in 176 seconds. The app allows 4 fresh investigations per person per hour and 12 a day overall.
- Security. Only ports 22, 80 and 443 are open. Sessions use a Secure, HttpOnly cookie with a hashed token and a 12-hour sliding expiry. GitHub sign-in asks for identity scopes only, uses the token for two API calls and discards it, and a state cookie blocks login CSRF.
- Resilience. Every service restarts automatically after a crash or reboot. Nightly database dumps keep the newest 14.
Known limits: about ten fresh investigations a day, slower responses than the planned ARM shape, and Oracle can reclaim free instances it considers idle. The hourly watcher keeps the server lightly busy.
Key design decisions
The project's credibility rests on a handful of decisions. The full decision log is kept in the repository.
| Decision | Why it matters to a buyer |
|---|---|
| Code computes, the LLM reasons (D4, D22) | No invented KPIs: every number traces to SQL or a deterministic function. |
| Governed metrics layer, not free-form text-to-SQL (D5) | Metrics are signed-off definitions; exploratory SQL exists but is labelled and sandboxed (D51). |
| Planted causes and decoys (D6) | Quality becomes a number: recall of real causes and decoys accepted. |
| Hypothesis-driven investigation shown in the UI (D7) | Reviewers see hypotheses, evidence for and against, and verdicts, not a black box. |
| Rule overrides in both directions (D23, D37, D54) | A confounded effect cannot be "supported"; strong evidence cannot be suppressed by a timid model; a claim with no significant effect is downgraded. |
| Every number in the summary is verified (D26) | Figures not found in the computed facts are rewritten or flagged. |
| Approvals enforced in the backend; agents cannot approve (D9, D28) | The same rules apply to the web app, an agent, or an external MCP client. |
| Tamper-evident audit log (D29) | SHA-256 hash chain; edits, inserts or deletes are detected. |
| Provider-agnostic LLM gateway, free tiers (D3, D12) | Runs on a local 7B model or Groq; model choice is a measured cost/quality trade-off. |
| Process mining in SQL, not PM4Py (D17) | Avoids AGPL licensing and keeps the stack lean. |
| A new process is configuration (D40) | Domain packs hold dimensions, metrics, monitors, actions, governance and documents. |
| Replay instead of re-running; rate limits only on token spend (D48, D49) | A public demo survives free-tier limits. |
| Accounts are created by an admin (D56) | GitHub proves who you are; the app decides who gets in. There is no sign-up route. |
| Net benefit is annualised from the months since a change began (D57) | A fix is no longer judged on a partial year. The Tuesday payment run now shows about ₹22.7 lakh a year, where it was once called not cost-effective. |
| Visitors are read-only (D58) | Anyone can look around without an account. |
| Showcase runs are recorded locally (D59) | Replays cost nothing and never spend the cloud quota. |
| Run on the free micro server, build elsewhere (D60) | Zero budget, and the ARM server was unavailable. |
| GitHub sign-in, identity only (D61) | No passwords to store, and the app cannot see repositories. |
| One model call at a time in production (D62) | Slower, but investigations finish inside the free per-minute limit. |
Build story
Nine milestones, each demoable on its own.
- M0–M3 · FoundationsRepo, Docker Compose, data generator, metrics layer, process mining, document retrieval with citations.
- M4 · InvestigateLangGraph investigation (plan, evidence, judge, recommend, summarise), what-if ROI.
- M5 · ActOperations MCP server, approvals, audit log, mock enterprise API.
- M6 · WatchKPI monitors with policy thresholds, alerts, alert to investigation, follow-up metrics.
- M7 · EvaluationGround-truth benchmark, retrieval metrics, multi-model comparison, evaluation report.
- M8 · Second packProcure-to-pay on the same engine; case study and demo script.
- M8 · PlatformAccounts and two-layer RBAC, built-in observability, policy-answer cache, light model tier, model comparison page; then investigation cache and showcase replay, rate limits, Analytics and Knowledge MCP servers, exploratory SQL, automatic alert triage.
- M8 · Richer data18 months per pack, seasonality, change points, a fifth cause and a new decoy per pack, 10 new documents.
- M8 · DeploymentPublic HTTPS deployment on a free Oracle instance, GitHub sign-in, showcase runs re-recorded on the new data, free-tier pacing for the cloud models.
Problems found and fixed along the way
- Scoped investigations were blind to their own cause. Asking "why are broker claims breaching?" compared brokers only with brokers. Fix: scoped checks also compare with the rest of the business.
- Tool calling failed on two cloud models. JSON-schema output succeeded 3/3 where tool calls did not; it became the default, with a fallback.
- A slow model server stalled runs for minutes. Per-read timeouts reset with every token; every call now has a wall-clock deadline, and output tokens are capped.
- Over-caution cost recall. The 120B model left real causes inconclusive (79% recall). Code-worded upgrades with strict evidence criteria took it to 100% without ever accepting a decoy.
- The second pack exposed hidden assumptions. Most of the work of adding procure-to-pay was moving process constants out of code into configuration.
- Observability found bugs on day one. Parallel assessment lost its context; cache hits were never recorded.
- Automatic triage, run live, failed honestly. The watcher container could not reach the models; the alert recorded "failed" with the reason, and the fix took one compose change.
- Richer data stressed the engine. A queue's month-by-month signal was blurred by another change, decoy signals crowded out a real cause, and a hypothesis with no significant effect stayed "supported". Three generic rule fixes (D54) resolved all three.
- The free ARM server was not available. Oracle's free Ampere capacity is shared and often exhausted. The demo moved to the 1 GB micro and the build moved off the server (D60).
- Port 80 timed out. The subnet's security list had no ingress rule for 80 and 443. A timeout means a silently dropped packet, while "No route to host" means the server's own firewall refused it, so the two point at different layers.
- A healthy app failed its health check. Six containers booting on an eighth of a core pushed the check past its 3-second timeout, and Caddy waits for a healthy backend. A 20-second timeout and a 3-minute start period fixed it.
- Parallel model calls hit the per-minute token limit. Four simultaneous calls used a minute's allowance at once. One call at a time with more retries finishes reliably (D62).
- A day's token quota was spent before launch. Re-recording showcase runs on Groq burned it. They are now recorded locally, as D59 intended.
- GitHub sign-in looked broken. The flow worked, but the accounts table was empty until the seed ran, and the app refuses unknown GitHub users by design.
Roadmap
The remaining work turns the live demo into a finished showpiece.
- OpenComplete the 18-month evaluation: local 7B model on both packs, three seeds, then rebuild the evaluation report.
- DoneRe-record showcase runs on the new data, so visitors can replay investigations without spending model quota.
- DonePublic deployment: HTTPS, production settings, a cloud model protected by the rate limits, and showcase runs shipped with it.
- DoneGitHub sign-in for visitors, with read-only access by default.
- OpenRe-run the cloud models on the 18-month data, paced for free-tier limits.
- OpenDemo video (about 4½ minutes) recorded against the live URL.
Deliberately out of scope: multi-tenancy, Kubernetes, streaming platforms, a third process pack.
Glossary
| Term | Plain meaning |
|---|---|
| SLA breach rate | Share of cases that missed the promised turnaround |
| Root cause (planted) | A problem deliberately built into the test data, so the answer is known |
| Decoy | A pattern that looks like a cause but is explained by something else |
| Confounder | The hidden factor behind a misleading pattern (e.g. claim value behind "health is slower") |
| Recall | Share of the real causes the system found |
| MCP | Model Context Protocol: a standard way for AI agents to use tools and data |
| Process mining | Reconstructing how work actually flows from system event logs |
| What-if | Estimating how many breaches a fix would have avoided, from the event log |