Knowledge Base

Tribal knowledge, captured.

Every project owns its own articles. Editors author in BlockNote — a block editor built on ProseMirror — and readers see a rendered surface that matches the Resolvd theme. Articles tie back to tickets through a suggestion ranker and a Promote-to-KB button on every resolved ticket.

Authoring

Block editor. No markdown gymnastics.

BlockNote handles the heavy lifting — rich text, headings, lists, code, callouts. Slash-command to insert a block, drag a handle to reorder. Persists as JSON in kb_articles.content_json with a plain-text mirror for full-text search.

Bundle adds ~380 KB gzipped — code-split pages/Kb* if CDN bandwidth matters.

Block primitives
Headings

H1 / H2 / H3 with anchor links auto-generated from slug.

Lists

Bulleted, numbered, and checkbox. Nestable.

Code

Inline and fenced blocks with syntax highlighting.

Callouts

Info / warning / tip / danger variants.

Tables

ProseMirror-backed, drag to reorder rows or columns.

Images

Drag-drop upload — stored alongside ticket attachments under FSL terms.

Version history

Every save. Restorable.

Every save snapshots into kb_article_versions with an optional change_summary. Restore any past version with one click — the restore itself writes a new version row marked Restored from vN so the audit chain never breaks.

Draft saved

Article persists to kb_articles.content_json with a plain-text mirror for full-text search.

Publish

Article becomes visible to readers in the project. Submitter / Viewer roles see the rendered surface; never the editor.

Edit + save

New row in kb_article_versions with optional change_summary. Old version stays restorable.

Restore old version

Writes a new version row marked Restored from vN. Audit chain stays intact — no destructive overwrite.

Archive

Hidden from listings but kept in storage. Tech / Manager / Admin can resurrect.

Tags + keywords

Two surfaces. One signal.

Tags drive the project article index — chip filters that AND together via tags @> $1::text[]. Keywords boost the trigram similarity ranker that matches articles to tickets. Use them for SKU codes, model numbers, error strings — searchable signal that would clutter a tag chip.

# Article frontmatter
title: "Reseat the RAM on a HP Z240"
tags: [hardware, hp, workstation]
keywords: [Z240, "DDR4", "blue screen", "0x7E"]
Index filter: tag chips
Ranker boost: keyword tokens
Search hit: either path
Ticket ↔ KB

Articles meet tickets where they live.

The Resolution tab on every ticket carries a Knowledge panel. Articles surface on open, link explicitly, or get drafted fresh from the ticket itself.

Suggestion ranker

pg_trgm similarity over title || tags || keywords vs the ticket title. High-confidence matches auto-surface in the Resolution tab on ticket open.

Promote-to-KB

Drafts a new article seeded from the ticket's title + description + resolution_summary, with a kind='system' link back to the source ticket.

Resolution summary nudge

At close time, a small prompt asks the closer to capture a one-line resolution summary. Drives the new "Fix applied" filter on the ticket list (resolution_summary IS NOT NULL OR kb_link EXISTS).

Agent-only articles

Some docs stay backstage.

Flip a single agent_only flag and the article disappears from listings, suggestions, and ticket links for anyone who isn't a project handler. Same gate as the Notes feature — global Admin / Manager / Tech, or a project member with a handler role override / Agent flag on that project.

For the "vault" docs: vendor credentials, bypass procedures, the printer reset codes the keyboard-warriors don't need.

Read

Project handlers only — global Admin / Manager / Tech, or any project member with a handler role override or Agent flag on the article's project.

List + search

Filtered out for non-handlers. 404 on direct slug fetch — keeps the slug invisible rather than leaking via 403.

Edit

Same gate as read. Tech / Manager with project-handler access can rewrite freely.

Tag aggregation

Agent-only tags excluded from the global tag chip row so topic hints don't leak to unprivileged users.

Ticket suggestion + links

Hidden for non-handlers in the Resolution tab ranker and the ticket↔KB link list.

Delete

Admin only. Tech / Manager handlers can read + write but can't delete an agent-only article.

Runbooks

A checklist with muscle memory baked in.

A runbook is a KB article with kind='runbook'. Same editor, same versioning, same agent-only flag. The difference is rendered on the ticket: a Runbook tab carries the checklist, with persistent state per ticket — not per user. Hand off mid-incident and the next handler picks up at the unchecked box where you stopped.

Inline tokens @canned:Vendor escalation become clickable 📋 pills that prefill the comment composer with the canned response — placeholders ({ticket.ref}, {submitter.firstName}) already substituted server-side.

# VPN keepalive incident — diagnostic runbook
[✓] Identify symptom — confirm VPN profile, capture TCP trace
— Devon Park · 5/10/2026
[✓] Pull TCP keepalive trace from monitoring
— Devon Park · 5/10/2026
## Mitigate
[ ] Schedule GPO revert (TCP keepalive 30 → 2 min), pilot on IT OU
[ ] If vendor needs to engage Outlook side, send: 📋 Vendor escalation
## Wrap up
[ ] Once mitigated, post customer-facing summary: 📋 Confirm root cause
Per-ticket state

Stored in ticket_runbook_runs. Step toggles run a Postgres jsonb || merge so concurrent handlers don't clobber each other.

Author once

BlockNote check-list-item blocks. Use the bracketed form @canned:[Title] for mid-line pills; bare form for end-of-step.

Audit trail

Each box records who ticked it + when. Reset wipes a single run; Mark complete stamps completed_at so handoffs can see the run was finished, not abandoned.

Capture the next fix.

Resolve a ticket → click Promote-to-KB → tweak → publish. The next time the same error walks in, the article auto-surfaces.