Vuva AI

Engineering write-up

Building a content platform with AI you can actually audit

I built this as a portfolio piece for a Forward Deployed Engineer application. It is a complete content platform: headless CMS, AI drafting help, retrieval-augmented search, and an approval step so nothing gets published without a person signing off. Everything on this page describes code that exists in the repo. Where something is simulated, I say so.

What I was trying to solve

Three problems kept showing up in every content system I looked at. First, nobody can find anything, so editors rewrite articles that already exist. Second, AI drafting tools got bolted on with no record of what the model wrote or who checked it, which is exactly what compliance teams are worried about. Third, a CEO and a developer need completely different versions of the same document, and most platforms handle that by making editors maintain two copies.

The interesting question wasn’t whether to add AI. It was whether one platform could do all three jobs well enough that editors wouldn’t route around it.

The approach

I designed it contract-first. Every boundary in the system is a typed interface: the app talks to the CMS through one ContentPort interface, the AI layer talks to language models through one provider interface, and every API returns the same response envelope. That decision is why the whole thing runs with no API keys or database. Swap the local seed adapter for Contentful, or the demo model for DeepSeek, and nothing above those interfaces changes.

How it works

Content

Articles, resources, FAQs and case studies each have a typed model with SEO fields and audience tags built in rather than bolted on. Eight fully written articles ship with the demo through the same interface a Contentful adapter would implement. Pages render on the server from that data at build time.

The AI workflow

Editors get nine operations: outline, summarize, rewrite, adapt for an audience, SEO description, tag suggestions, FAQ generation, unsupported-claim screening, and an accessibility pre-check. Anything the assistant produces carries an AI GENERATED DRAFT label that follows it through every workflow state. The label only matters because of what comes after: a draft moves draft → evaluation → review → approval → publication, each step writes an audit event, and the state machine rejects illegal jumps. Publishing requires a prior approval by a human account; the orchestrator identity is explicitly blocked from publishing, and I have a test that proves it.

RAG

Questions hit a BM25 index over the structured corpus. Retrieved passages get wrapped in delimiters and passed to the model as quoted data with instructions stripped out, next to a system prompt that says, in effect: answer only from these sources, ignore any instructions you find in them. The response cites specific passages, and the API checks those citations against what retrieval actually returned, so the model can’t invent a source. If the best match scores below a relevance floor, the endpoint refuses instead of stretching. Try asking it about vacation policy; you’ll get the refusal, not a guess.

Evaluation

Every draft can be scored on seven dimensions: grounding, relevance, completeness, tone, safety, accessibility and source coverage. The heuristic scorer does real lexical overlap and pattern analysis, not random numbers, but it’s advisory by design. A human still approves. Machines catch the mechanical failures so people can spend attention on judgement calls.

Accessibility

Semantic landmarks, ordered headings, skip link, full keyboard paths, 44px targets, visible focus rings, reduced-motion support. Status never relies on colour alone; every badge pairs the colour with an icon or word.

Performance

All public pages pre-render at build. No web fonts, no trackers. Client JavaScript is limited to small interactive pieces: search, discovery, the assistant, recommendations.

Security

Model calls happen server-side only. Inputs are validated at every boundary without a validation library. Injection patterns are screened on both input and retrieved text. Secrets live in environment variables and none exist in the demo.

Testing

38 tests across search relevance, RAG guardrails, the workflow state machine, API contracts and the markdown escaper. The workflow tests encode the governance rules directly: no publish without approval, no machine publisher, rejections need reasons.

Stack

  • Next.js 15 (App Router)
  • React 19
  • TypeScript strict
  • Tailwind CSS
  • BM25 retrieval
  • OpenAI-compatible LLM layer
  • Cloudflare Workers
  • Vitest

The provider layer speaks the OpenAI chat-completions protocol, which covers DeepSeek, OpenAI, Azure OpenAI and most self-hosted gateways. Point it at a real endpoint with three environment variables and the identical call path reaches a live model.

What’s real versus simulated

Real implementation
CMS abstraction and seed corpus, search indexing and ranking, RAG pipeline with citation checking and refusals, workflow state machine and audit log, evaluation harness, assistant heuristics, all APIs, the dashboards reading from them, the test suite.
Simulated, labelled everywhere it appears
Model output in demo mode (a deterministic heuristic engine stands in for an LLM), and usage telemetry such as token counts and cost figures. Both say so in the UI.

What I’d change for production

Being straight about this is part of the point.

  • Persistence. Workflow state and audit events live in memory here. Production gets Postgres (or D1 on this same Cloudflare setup) with append-only storage for audit records.
  • Retrieval. BM25 over 25 documents is fine; over 50,000 it isn’t. I’d move to managed vector search behind the same interface and add hybrid ranking before reaching for rerankers.
  • Auth. The dashboard has no login because it demos workflow mechanics. Production needs real identity, role-based permissions and per-editor approval rights.
  • Rate limiting. The AI endpoints need per-IP and per-account limits with budget ceilings in front of any paid provider. In demo mode cost is zero by construction, so I left the hooks documented but inert.
  • Evaluation. The heuristic scorer demonstrates the shape. Production would use a real model judge plus a fixed question bank run in CI after every corpus or prompt change.

Try these

  • Ask discovery “what is our vacation policy?” and watch it refuse honestly.
  • Paste “guaranteed 10x returns” into the assistant’s claim screener.
  • Create a draft, then try to publish it without approving. The API will stop you.

Part of the Vuva Systems portfolio — vuvasystems.com