# The Knowledge Base

2026-08-08. Decided as an **application** on the same day, in
[the road to cutover](2026-08-08-the-road-to-cutover.md): the KB is a
first-party Convoro application, not a markdown tree in `docs/`.

It is on the cutover gate twice — once as an application to build, and once as
a body of writing to produce. This note is about the application. What goes in
it is written per application as that application ships, rather than as one
documentation phase at the end.

---

## Why an application and not `docs/`

Four reasons, in order of weight:

1. **The assistant needs a queryable corpus.** Files in git are not reachable
   from a running site without inventing a build step and a shipping format.
   Content in the database is reachable through the content-type registry,
   which already exists and already backs search.
2. **It dogfoods the platform.** If Convoro's own documentation cannot live on
   Convoro, that is information worth having early.
3. **It is public.** Documentation nobody can read answers no support questions
   and ranks in no search engine.
4. **It is sellable.** Every mature platform has one.

`docs/design/` does not move. Design notes are provenance and stay in git.

## 🚨 Written to be retrieved from, not only read

This is the constraint that shapes the schema, and it is the one thing about
this application that is genuinely different from Pages or Blog.

An article will be handed to a retriever, which will return **three of them at
once** to answer a question the writer never saw. So:

- **One answer per article.** An article covering four things retrieves for all
  four and answers none of them well.
- **Self-contained.** An article that only makes sense after reading the four
  before it is a bad article and a *useless* retrieval result.
- **Titled as the question somebody would ask.** "Why does my logo not appear?"
  retrieves; "Branding" does not.

Two columns exist for this and for nothing else:

- **`question`** — the natural-language question the article answers, separate
  from `title`. A page needs a short title in a sidebar; a retriever needs the
  question. Forcing one field to be both makes it bad at one of them.
- **`summary`** — a two-sentence answer, written by hand. 🚨 This is what the
  assistant quotes when the whole article is too long for the context it has.
  An excerpt sliced off the top of the body is a sentence that happened to be
  first, which is not the same thing as an answer.

## Audience is a column, not a category tree

Developer, administrator and end user. It is tempting to make these three
top-level categories, and that is wrong: an article about widgets has an
administrator version and a developer version, and they are not in different
*subjects*, they are the same subject for different people.

So `audience` is a column and categories are about subject matter. That lets
the front end offer "show me the administrator articles" across every category,
and lets the assistant weight by who is asking — a member asking about widgets
should not be handed the extension API.

## Visibility

The same rule as everything else, and by now it should be boring: a group list,
empty meaning everybody, enforced in the service rather than in templates.

🚨 And the same consequence the custom-database note ran into: **only articles
visible to everybody go into the search index**, because a FULLTEXT index
cannot be filtered per viewer. The *article list* is filtered per viewer, which
is where the interesting case actually is — internal staff documentation living
alongside public documentation is the whole reason the field exists.

## "Was this helpful?"

Two counters on the article and a row per vote, so one person voting twice
counts once. The row is what makes it honest; the counters are what make the
admin list sortable without an aggregate per article.

🚨 **No free-text feedback box in this version.** It is the obvious next
request, and it needs moderation, notification and a place to read it —
otherwise it is a table that fills up and nobody looks at. Its own change.

## What it registers

Everything the registries offer, which by now is the pattern:

- **Content type** so articles are searchable.
- **A widget kind** — "Knowledge Base articles" — so the most-read articles can
  sit on the forum index.
- **Widget areas** on the index and on an article.
- **Personal data**, for the votes, which name a member.

## URLs

`/kb` for the index, `/kb/{category}` for a category, `/kb/{slug}` for an
article. 🚨 An article and a category share one namespace under `/kb`, which is
the same collision Pages and custom databases have at the root — so the same
answer: one route, and the controller tries a category then an article. Two
routes for one pattern is the mistake that was nearly shipped last time.

Not at the site root. `/kb` is a real prefix rather than a fallback, because
documentation is a section rather than a page, and it keeps the root namespace
for the things that genuinely live there.

## What this note does not decide

- **Article revisions.** Documentation is edited more than anything else on a
  site, and "who changed this and when" is a real question. Its own change.
- **Ordering by helpfulness on the front end.** The counters exist; whether a
  reader should see them is a separate argument.
- **How the assistant retrieves.** Keyword first, measured against real
  questions before anything vector-shaped — decided in the road-to-cutover
  note and unchanged here.
- **Translations.** The KB is the part of the product most likely to want them.
