Skip to content

Review drafts & grounding

A fact with lifecycle_status: 'draft' is unreviewed. Drafts are visible by default.

// Hide drafts for one call…
await wiki.read('user-1', 'deploy process', { excludeDrafts: true });
await wiki.traverseGraph('user-1', { sourceId, excludeDrafts: true });
// …or engine-wide
createWiki(db, { llmProvider, config: { excludeDrafts: true } });
  • Drafts are removed before the maxResults, tierFloors, and pre-filter cuts, so they never take a slot from a reviewed fact.
  • In traversal, a draft is a dead end. The anchor fact is always returned.
  • Status is read from SQLite on every call, so a promotion takes effect immediately.
const { facts, nextCursor } = await wiki.listDrafts('user-1', { limit: 50 });
await wiki.promoteDraft(facts[0].id, 'user-1', { by: 'human:alice' });
// → stable; trust tier 'human-reviewed' because `by` starts with 'human:'
// Reject instead:
await wiki.setLifecycleStatus(facts[1].id, 'user-1', 'deprecated'); // or forget()

promoteDraft throws WikiDraftNotFound if there’s no live draft with that ID. Promotion doesn’t change updated_at, so the fact keeps its place in recency ordering.

Grounding makes LLM writers quote the text they were shown. A deterministic check (no LLM involved) verifies each quote:

createWiki(db, {
llmProvider,
config: {
grounding: {
mode: 'draft', // 'off' (default) | 'draft'
writers: ['ingest'], // default; you can add 'librarian' and 'heal'
minEvidenceChars: 20, // shorter quotes count as missing
maxEvidence: 3, // quotes requested per fact
maxEvidenceChars: 300,
},
},
});
Outcome Stored as Diagnostic
Every quote is found stable, trust tier machine-confirmed —
No usable quotes draft grounding_missing (no_evidence / evidence_too_short)
A quote isn’t found, or there are more than 10 quotes draft grounding_failed (quote_not_found / too_many_quotes)
  • Ingest: the chunk text.
  • Librarian: each event’s summary in the prompt.
  • Heal: recent event summaries, plus the bodies of non-draft document anchors (clipped to 800 characters).
  • Instructions, the manifest, existing facts, and IDs never count, so a model can’t “ground” a claim by quoting its own instructions.

Matching normalizes both sides with NFKC, collapses whitespace runs, and is case-sensitive. The quotes themselves aren’t stored.

upsertGraph() nodes come from your own code, so they’re never grounded.