OpsDesk AI is a policy-gated AI service desk demo for local business operations: messy customer enquiries become structured tickets, retrieval-aware drafts, approved-template responses, human review queues, and Watchtower audit trails.
Live demo: opsdesk-ai.netlify.app
If you only have a few minutes:
/case-study- business problem, product story, AI boundary, and architecture summary./enquiry- submit a messy customer enquiry and trigger AI triage./dashboardand/tickets/:ticketId- inspect ticket state, draft response, follow-ups, and policy decisions./watchtower- review prompt/model metadata, validation, audit events, cost, latency, and approval status./platformand/architecture- see how the hosted demo maps to Postgres, pgvector, queues, webhooks, observability, search, and graph boundaries.
The /case-study route includes the ambiguous business problem, workflow design, data model, AI policy gate, audit trail, outcome, product screenshots, a short animated workflow GIF, and an inline architecture diagram.
Visual assets are committed under public/showcase/:
dashboard.png- service desk screenshotwatchtower.png- AI audit screenshotarchitecture.png- architecture route screenshotopsdesk-flow.gif- short dashboard to ticket to Watchtower walkthrough
This project reflects the same implementation pattern I use in enterprise software delivery: understand the real operational workflow, protect data integrity, automate the repeatable path, keep exceptions reviewable, and design the result so it can be demoed, supported, and extended.
The seeded business is DragonTech Facilities. The app includes five realistic cases:
- CCTV quote request
- Office Wi-Fi issue
- Invoice/delivery note mismatch
- Insurance certificate expiry
- Angry customer complaint
AI behaves like a junior operations assistant. It can classify, summarise, extract, select approved response templates, and suggest follow-ups. If confidence is high and the case is only asking for missing facts, it can auto-send a pre-approved message/form through the customer's preferred channel. Complaints, urgent incidents, document/payment mismatches, low-confidence outputs, closes, refunds, and price commitments stay with a human reviewer.
The expanded Agent Delivery and Platform routes also show the consulting and MLE story: client discovery, success metrics, agentic solution design, LLM prototyping, RAG with pgvector, event contracts, webhooks, evaluation, production rollout, monitoring, and ROI measurement.
flowchart LR
intake[Ticket intake] --> triage[AI triage]
triage --> rag[Knowledge / RAG]
rag --> gate[Policy gate]
gate -->|approved template| event[Event / webhook]
gate -->|risky case| review[Human review]
event --> audit[Audit / Watchtower]
review --> audit
The AI boundary is explicit: approved templates can auto-send only for high-confidence missing-information requests. Risky cases queue for review, including urgent incidents, complaints, payment or document mismatches, low-confidence outputs, refunds, closes, and price commitments.
- Full-stack TypeScript with React 19 and React Router framework mode
- PostgreSQL data model through Drizzle schema, with pgvector-ready knowledge chunks for RAG
- Vercel AI SDK-ready boundary with deterministic demo AI for public reliability
- pg-boss worker scaffold for triage, extraction, retrieval, evaluation, and webhook jobs
- Vitest unit coverage and Playwright route smoke tests
- Custom shadcn-inspired UI system
pnpm install
pnpm devOpen the local URL shown by React Router.
The app works immediately from seeded demo data in code. Local clicks are stored in the same cookie-backed fake session store used for the hosted demo, and Reset demo clears that browser session.
Runtime mode is controlled by OPS_DESK_MODE=demo|local|prod and defaults to demo. Demo mode does not require Docker, databases, cloud credentials, or outbound integrations.
The app is configured for Netlify with @netlify/vite-plugin-react-router and netlify.toml.
pnpm install
pnpm buildNetlify should use:
- Build command:
pnpm build - Publish directory:
build/client
The public demo uses a cookie-backed fake session store. Each visitor starts from the seeded demo state, and their clicks are replayed in their own browser session without requiring a database.
Optional Netlify environment variable:
SESSION_SECRET- signs the demo session cookie.
The production roadmap is visible in the app and now includes concrete adapter work, not only copy:
- Auth/RBAC: tenant-aware sessions and roles for operator, manager, auditor, and integration admin.
- Observability: Watchtower traces can be converted to OTLP/JSON payloads using
OTEL_SERVICE_NAMEandOTEL_EXPORTER_OTLP_*config. - Evals: expand fixture regression packs for template selection, retrieval citations, policy gates, and confidence thresholds.
- Queue retries: promote pg-boss contracts into workers with exponential backoff, DLQ replay, and idempotency checks.
- Customer configuration: persist customer-specific channels, templates, SLA rules, risk settings, and webhook destinations.
Netlify, standard CI, and the default pnpm dev path do not use Docker profiles. The local production runtime is optional and documented in docs/architecture/local-runtime.md.
Copy optional runtime settings when you want to exercise local production-style dependencies:
cp .env.example .envSet OPS_DESK_MODE=local in .env when you want the app to check local dependency configuration instead of staying in seeded demo mode.
Start only the services you need. Postgres/pgvector is the first local adapter target:
docker compose --profile postgres up -d
pnpm db:push
pnpm seed
pnpm workerpnpm seed remains safe for demo mode. In local mode it also upserts the demo business, tickets, documents, AI runs, knowledge chunks, audit events, and agent/evaluation records into Postgres without duplicating rows.
Other optional profiles are independent:
docker compose --profile events up -d
docker compose --profile search up -d
docker compose --profile graph up -dYou can combine profiles when you need more of the local stack:
docker compose --profile postgres --profile events --profile search --profile graph up -dSimple wrappers are available for the common local path:
pnpm local:up
pnpm local:seed
pnpm local:worker
pnpm local:downUse pnpm local:reset only when you want to delete local service volumes.
The Drizzle schema lives in app/shared/db/schema.ts. It includes tickets, response templates, AI runs, client discovery sessions, agent blueprints, vector knowledge sources/chunks, and evaluation runs. The compose database uses db/init/001-pgvector.sql to enable the vector extension on a fresh volume. The pg-boss worker scaffold lives in scripts/worker.ts.
Phase 3 adds explicit boundaries under app/shared/adapters:
- Runtime config:
OPS_DESK_MODE, dependency checks, and outbound-webhook opt-in. - Event bus: publish, subscribe/dispatch, retry metadata, idempotency, and DLQ records.
- Observability: trace and span metadata that links Watchtower, events, and webhook attempts, plus OTLP/JSON export payload mapping.
- Webhooks: canonical payloads, HMAC signatures, idempotency keys, retry scheduling, and no real outbound calls unless configured.
- Search: document indexing, faceted search, and read-model rebuild contracts.
- Graph: relationship node/edge listing and predefined graph query answers.
Demo implementations are deterministic and credential-free. Local/prod factories fail with clear configuration errors until the corresponding Redpanda, OpenSearch, or Neo4j adapter is implemented and configured.
Terraform skeletons live in infra/terraform/ and are safe to inspect only; they do not configure providers, account IDs, remote state, or deployable resources. Kubernetes manifests live in k8s/ and document a future web/worker deployment shape. They are not part of required verification, and the Kubernetes path requires real health endpoints before apply.
- Product workflow:
/dashboard,/enquiry, and/tickets/:ticketIdshow intake, triage, drafts, document extraction, and follow-ups. - AI safety/evaluation:
/watchtowershows prompt versions, validation status, approval gates, costs, and demo trace details. - Observability:
/watchtowerlinks AI runs to trace spans, events, and webhook attempts, with OTLP/JSON payloads ready for an OpenTelemetry collector. - Event-driven architecture:
/eventsshows Kafka/Redpanda-shaped envelopes, schema versions, idempotency keys, consumer lag, retries, and DLQ context. - Webhooks/integration:
/webhooksshows endpoint ownership, signed payloads, timestamp tolerance, idempotency, secret rotation, retries, inbound examples, and replay. - CI/CD/testing:
/platformmaps unit, typecheck, build, route smoke, preview deploy, and release gates. - Cloud/IaC/Kubernetes path:
/platformseparates hosted demo mode from AWS serverless, Terraform, container, and Kubernetes deployment boundaries. - Search/graph/data modeling:
/knowledgeshows faceted retrieval, OpenSearch mapping, RAG citations, and graph queries backed by ticket, document, and AI-run evidence.
Hosted demo behavior is deterministic and credential-free. Local/prod mode swaps the same contracts onto Postgres, pgvector, pg-boss workers, OpenSearch, queues/topics, tracing exporters, and signed integration endpoints.
/dashboard- ticket queue and operational overview/enquiry- public enquiry widget simulation/tickets/:ticketId- ticket workspace with AI draft approval and document extraction/watchtower- AI run monitoring and audit log/agent-delivery- client discovery, agent framework, pgvector/RAG, rollout, and ROI showcase/platform- hosted demo versus production path, CI/CD, IaC, Kubernetes, observability, and data contracts/events- Kafka/Redpanda-style event contract simulator with lag, retry, DLQ, schema, idempotency, and correlation IDs/webhooks- endpoint registry, HMAC signature preview, delivery attempts, retry schedule, inbound examples, and replay/knowledge- faceted search, OpenSearch mapping preview, RAG citations, and graph relationship queries/case-study- portfolio-ready case study page/architecture- build plan, queue contract, and table map


