1The problem
In tech companies, two kinds of content live apart: the technical documentation (what the product actually does) and the help-center articles (what the end user reads). When the product changes, someone has to remember to update the documentation — and then remember which articles that change affected, and rewrite each one by hand. That loop breaks all the time: the docs move forward and the articles quietly go stale.
We wanted to close that loop with AI, but without taking the human out of the decision. The idea: the technical doc is the single source of truth and lives inside the app itself. When a new version of a doc is uploaded, the system figures out which articles were impacted and drafts the revisions — but nothing goes live without a person approving it. The AI does the heavy lifting; the human keeps the final say.
2The stack
"Stack" is the word for the set of software pieces that hold a product up. BaseViva's guiding choice was to lean on a managed cloud stack and let it handle the plumbing, so the effort went into the AI and the impact detection. The front is a web app in React (built with Vite); the backend is AWS Amplify Gen 2 — a toolkit that wires up login, database and API from TypeScript code. See the term for the plain-language version.
The login runs on Amazon Cognito, and the whole API is an layer (AppSync) generated automatically from the data models — there is no hand-written CRUD and no separate API gateway. The data lives in , and every model is isolated : each user only ever sees their own data. The generative AI runs on , reached through a handful of small functions.
The React app in the browser. Two things run right here on the client: breaking the markdown into sections (chunking) and the impact detection itself, computed over vectors loaded from the server.
Login by email through Amazon Cognito. The token it issues carries the identity that marks every record as belonging to that user.
The entire API is a layer that Amplify generates from the data models — no hand-written CRUD, no separate API gateway.
Ten models in , each isolated . Among them, the Embedding model stores one item per section vector.
Four small functions — embed, rewrite, outline and draftArticle — exist for one reason only: to call the AI. Each has the minimum permission for the model it uses.
runs two models: Titan v2 to turn text into vectors (embeddings) and Claude to write the article drafts.
A deliberate note on where the logic runs: the AI (on Bedrock) and the storage (in DynamoDB) live on the server, but the markdown chunking and the impact detection are pure functions that run in the browser, over vectors loaded on demand. That split matters for the next section.
3How we used AI
The AI shows up in two moments. The first is when you upload a doc for the very first time: there are no articles yet, so the AI proposes the initial ones. The second — the heart of the product — is when you upload a new version of a doc that already has articles: the system detects what changed, finds the impacted articles and drafts the revisions. Tap each step to follow that second flow.
You upload a new version of a technical doc (Markdown or .docx). The app extracts the text and keeps the original file; each upload becomes a new version, never overwriting the previous one.
The markdown is split into sections by its headings, and the system compares the old version with the new one section by section to see exactly which sections changed. This happens over per section, not over the whole document.
Each article remembers which section of the doc it came from (its ). So the impacted articles are simply the ones linked to a section that changed — a reliable signal, instead of a fragile article-vs-doc similarity score.
For each impacted article, the AI (Claude, on Bedrock) writes a revision as a draft — a . It never overwrites the published article; it produces a proposed version plus a short summary of what changed.
A person reviews the proposal next to a diff against the published version, and decides: approve, edit and approve, or reject. Only on approval does a new published version of the article get created. Nothing goes live on its own.
On the first upload of a doc, the flow is a little different: there are no articles yet, so you choose how to generate them — the whole doc as one article, or several articles split by its headings. Either way, each draft comes in as a proposal of type "new article", and the same human review decides whether it becomes a published article.
4Key decisions
Keep a human in the loop, always. It would have been simpler to let the AI publish its revisions automatically. We chose the opposite: the AI only ever produces a draft, and nothing reaches the end user without a person approving it. A help center is a promise to the customer — getting it wrong at scale is worse than getting it updated a little slower.
The technical doc is the single source of truth, inside the app. Instead of depending on Git or external services, the source of truth is the file you upload — and the trigger for everything is simply uploading a new version. No integration to set up, no pipeline to babysit. Upload a doc, and the loop starts.
Upload instead of GitHub. The original premise was different: the docs would live in a GitHub repository, and a push there would trigger the update. We dropped that on purpose — a Git integration brings auth, webhooks and a whole setup step before anyone gets any value. Since this was built live in a course, we chose the path that gets to the point fastest: the person uploads the technical doc straight into the platform, and the AI generates the articles from it. The model still treats the upload as "a new version", so wiring Git back in later is an addition, not a rewrite.
Never overwrite — always version. Both technical docs and articles are versioned: a new upload or a new approved revision creates a new version, the previous one stays. That is what lets the system show a diff, keep an audit trail, and let a human compare the published article with the proposed change side by side.
Work by section, not by whole document. Real technical docs are large. Treating a doc as one big blob breaks in several ways, so we made the the unit of work — for splitting into articles, for embeddings, and for impact detection. This one decision is what makes the rest actually work; the next section explains why.
5Getting more technical…
This section is for whoever wants to look under the hood. None of it is required to understand the product — but these decisions are the ones that made impact detection reliable instead of flaky.
Impact detection anchored in the section, not in article-vs-doc similarity. Our first instinct was to compare each help article against each doc section by . Real measurement killed that idea: a friendly article and a dry technical section score low even when they're about the same thing, so any threshold on that scale is fragile — it caused a "nothing gets proposed" bug. The reliable signal is different: comparing doc v1 against doc v2, section by section (same kind of text), separates cleanly. So the real trigger became: each article records the heading it came from; when that section changes between versions, the article is impacted — directly, without the flaky score.
One embedding per section, stored one row at a time. An embedding of the whole document is truncated by the model's input limit and dilutes a localized change into a single vector — two versions can even look identical because both truncate at the same beginning. So we embed each section on its own. Each (about 1024 numbers, ~21 KB) is stored as one row in the database, indexed by the version it belongs to. An earlier attempt kept all of this in the browser's local storage and blew past its ~5 MB quota on big docs; moving it to the server fixed that for good.
Split the markdown programmatically, use the AI only for titles. Asking the AI to return a JSON with the text of every section blows past the output token limit — the JSON comes back truncated, the parse fails, and it falls back to "one article". So the split by headings is done deterministically in code, and the AI is used only to improve the article titles, never to return the content. A small change of responsibility that quietly removed a whole class of failures.
Least-privilege permissions per AI function. Each can only call the exact model it needs: the embedding function reaches only Titan; the writing functions reach only Claude. No secrets live in the repository — the functions talk to the AI through their execution role, never through a key in the code.
6What we learned
Measure before you trust a metric. We assumed semantic similarity between an article and a doc would be a good impact signal. It wasn't — and only real measurement showed it. The lesson with AI is to validate the signal against actual numbers before building a whole feature on top of it.
Granularity decides everything. Working at the document level failed on three fronts at once — truncated embeddings, broken JSON, invisible progress. Dropping down to the section level fixed all three. With AI, the size of the unit you process is often more important than the prompt itself.
Long AI operations need visible progress. Generating N articles or embedding N sections takes time, and a single global loading spinner feels like a frozen screen. Breaking the heavy work into small steps and showing "item X of N" turns the wait into something that feels like it's being built — the same lesson we carry across every product.
7What this means for your company
The BaseViva idea — keep a single source of truth, detect what each change impacts and let AI draft the revision, always with a human approving — isn't exclusive to help centers. It's a pattern you can borrow: close the gap between a source that moves and the content that depends on it, without taking the person out of the decision. Here's how it travels to other contexts.
When a policy, contract template or product spec changes, the system flags every downstream document — FAQs, playbooks, training material — that the change touches, and drafts the update for a reviewer to approve. No more stale manuals nobody noticed.
Keep the team wiki honest: when the code or the runbook changes, the onboarding docs and internal guides that reference it are flagged and a revision is drafted — so the knowledge a new hire reads matches what the team actually does today.
When a course, a tool or a reference changes, the lessons, scripts and support material that depend on it are flagged for revision — so a body of educational content stays current without re-checking every file by hand.
The takeaway: wherever content derives from a source that keeps changing, detecting impact and drafting the revision — with a human approving — beats remembering to update everything by hand. That's the piece worth adapting to your context.