# Knowledge Base backend contract — v5.1

## Principle
Production frontend does not own article text. MySQL stores category tree, article identity, immutable versions, audiences, contextual help links, relations and feedback. `assets/js/kb-data.js` is only a static demo fallback so the prototype opens without a backend.

A new draft must not make an already published article disappear. Therefore publication status belongs to **the version**, while `kb_articles.current_published_version_id` continues to point to the last valid published version until a new version is published.

## Public/read API
- `GET /api/v1/kb/categories`
- `GET /api/v1/kb/articles?q=&category=&page=` — server performs tenant, visibility and audience filtering before returning results.
- `GET /api/v1/kb/articles/{slug}` — resolves tenant override first, otherwise global article; returns current published version and related articles.
- `GET /api/v1/kb/context?route=teacher/review.html&context=evidence` — resolves contextual help from `kb_context_links`.
- `POST /api/v1/kb/articles/{slug}/feedback` with `{helpful:boolean|null, comment?:string}`.

## Editorial API
- `POST /api/v1/kb/articles`
- `POST /api/v1/kb/articles/{id}/versions`
- `PATCH /api/v1/kb/versions/{id}` — only `DRAFT` or `IN_REVIEW` according to role policy.
- `POST /api/v1/kb/versions/{id}/submit-review`
- `POST /api/v1/kb/versions/{id}/approve`
- `POST /api/v1/kb/versions/{id}/publish`
- `POST /api/v1/kb/context-links`
- `PATCH /api/v1/kb/context-links/{id}`
- `POST /api/v1/kb/relations`

## Versioning
Workflow: `DRAFT → IN_REVIEW → APPROVED → PUBLISHED → RETIRED`.
A `PUBLISHED` body is immutable. Editing creates `version_no + 1`. The previous published version remains live until the new version reaches `PUBLISHED`, then `current_published_version_id` is switched transactionally.

## Context help
The cabinet requests help by route and optional component/action key. Mapping is data in `kb_context_links`; therefore a methodist/admin can change contextual help without rebuilding frontend.

Resolution order:
1. tenant + exact route + exact context;
2. tenant + exact route without context;
3. global + exact route + exact context;
4. global + exact route;
5. product default article.

## Search
Initial implementation: MySQL FULLTEXT over `title`, `excerpt`, `search_text`. Authorization and tenant filtering occur before response serialization. Search index text is derived from sanitized article content.

## Tenancy and visibility
- Global KB: `tenant_id IS NULL`.
- Tenant-specific article may override a global slug for that tenant.
- `PUBLIC` is readable without cabinet login.
- `AUTHENTICATED` requires a session.
- `ROLE_SCOPED` also requires a matching `kb_article_audiences` row.
- `INTERNAL` is for operational/editorial documentation only.
- `tenant_id` and roles are derived from authenticated session; client-supplied tenant identifiers are not trusted.

## Security
`body_html` is sanitized on write. Restricted/safety data is never embedded in KB articles. Editorial writes and publications are audit logged by the platform backend. Rate limits apply to public search and feedback endpoints.
