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.
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.
H1 / H2 / H3 with anchor links auto-generated from slug.
Bulleted, numbered, and checkbox. Nestable.
Inline and fenced blocks with syntax highlighting.
Info / warning / tip / danger variants.
ProseMirror-backed, drag to reorder rows or columns.
Drag-drop upload — stored alongside ticket attachments under FSL terms.
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.
Article persists to kb_articles.content_json with a plain-text mirror for full-text search.
Article becomes visible to readers in the project. Submitter / Viewer roles see the rendered surface; never the editor.
New row in kb_article_versions with optional change_summary. Old version stays restorable.
Writes a new version row marked Restored from vN. Audit chain stays intact — no destructive overwrite.
Hidden from listings but kept in storage. Tech / Manager / Admin can resurrect.
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.
tags: [hardware, hp, workstation]
keywords: [Z240, "DDR4", "blue screen", "0x7E"]
▸ Ranker boost: keyword tokens
▸ Search hit: either path
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).
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.
Project handlers only — global Admin / Manager / Tech, or any project member with a handler role override or Agent flag on the article's project.
Filtered out for non-handlers. 404 on direct slug fetch — keeps the slug invisible rather than leaking via 403.
Same gate as read. Tech / Manager with project-handler access can rewrite freely.
Agent-only tags excluded from the global tag chip row so topic hints don't leak to unprivileged users.
Hidden for non-handlers in the Resolution tab ranker and the ticket↔KB link list.
Admin only. Tech / Manager handlers can read + write but can't delete an agent-only article.
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.
— Devon Park · 5/10/2026
— Devon Park · 5/10/2026
Stored in ticket_runbook_runs. Step toggles run a Postgres jsonb || merge so concurrent handlers don't clobber each other.
BlockNote check-list-item blocks. Use the bracketed form @canned:[Title] for mid-line pills; bare form for end-of-step.
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.