<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged knowledgebase</title>
<link>https://coderlegion.com/tag/knowledgebase</link>
<description>Powered by Question2Answer</description>
<item>
<title>How Synthadoc Handles Hallucination and Why &quot;Don't Hallucinate&quot; Isn't Enough</title>
<link>https://coderlegion.com/25713/how-synthadoc-handles-hallucination-and-why-dont-hallucinate-isnt-enough</link>
<description>&lt;p&gt;Ask most AI products how they handle hallucination and you'll get a variant of &quot;we tell the&lt;br&gt;
model not to.&quot; That's not wrong — it's just insufficient, and it puts enforcement in the&lt;br&gt;
hands of the exact system doing the generating.&lt;/p&gt;
&lt;p&gt;Synthadoc treats hallucination as an &lt;strong&gt;architecture problem&lt;/strong&gt;, not a wording problem. The&lt;br&gt;
distinction matters: a wording fix is hoping the model behaves; an architecture fix is&lt;br&gt;
verifying that it did.&lt;/p&gt;
&lt;h3 id=&quot;why-prompt-instructions-aren-t-enough&quot;&gt;Why Prompt Instructions Aren't Enough&lt;/h3&gt;
&lt;p&gt;The industry default is a line in a system prompt: &lt;em&gt;don't make things up, answer only from&lt;br&gt;
the provided context, never fabricate.&lt;/em&gt; These instructions do nudge model behavior in the&lt;br&gt;
right direction, and we use them too. But they leave enforcement entirely up to the model&lt;br&gt;
doing the generating. A language model producing text token-by-token has no reliable&lt;br&gt;
internal mechanism to audit its own faithfulness to a source.&lt;/p&gt;
&lt;p&gt;There's a subtle issue as well. Instructions of the form &quot;don't do X&quot; are easier for a&lt;br&gt;
model to rationalize around than instructions of the form &quot;do only Y.&quot; Synthadoc's synthesis&lt;br&gt;
constraint is a positive formulation:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Answer using ONLY these wiki pages. Cite with [[PageTitle]].
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That's linguistically harder to rationalize around. But it's still just text in a prompt.&lt;br&gt;
The real work happens at the structural level.&lt;/p&gt;
&lt;h3 id=&quot;three-layers-not-one-instruction&quot;&gt;Three Layers, Not One Instruction&lt;/h3&gt;
&lt;p&gt;Our approach treats hallucination as a systems problem. There are three distinct layers,&lt;br&gt;
each handling a different failure mode:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Scope filtering at ingest time&lt;/strong&gt; : Wiki content can only be entered if it is on-domain. It is difficult to produce hallucinations about off-domain topics from content that has never been consumed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Retrieval-augmented grounding at query time&lt;/strong&gt; : Answers are generated from retrieved&lt;br&gt;
wiki pages, not from the model's weights alone. The synthesis prompt shows only the&lt;br&gt;
relevant pages and instructs the model to answer from them exclusively.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Citation faithfulness audit after the fact&lt;/strong&gt; : Every claim gets a citation marker&lt;br&gt;
linking it to specific source lines. A separate LLM pass then audits whether each claim&lt;br&gt;
is actually supported by those lines. This is the layer that catches what the other two&lt;br&gt;
miss.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;None of these is a complete solution alone. Together they create a system where hallucinations&lt;br&gt;
are &lt;em&gt;detectable and traceable&lt;/em&gt;, not just discouraged.&lt;/p&gt;
&lt;h3 id=&quot;layer-1-purpose-md-as-a-content-gate&quot;&gt;Layer 1: purpose.md as a Content Gate&lt;/h3&gt;
&lt;p&gt;Every wiki has a &lt;code&gt;wiki/purpose.md&lt;/code&gt; file that describes its domain — what it's for, what&lt;br&gt;
topics belong, and what doesn't. When a new source document arrives for ingest, the ingest&lt;br&gt;
agent reads this purpose block before making a decision:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Wiki scope (from purpose.md):
{content of purpose.md}

action=&quot;skip&quot; means the source is completely OUTSIDE the wiki's domain
(e.g. spam, medical receipts, unrelated e-commerce).
…
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The LLM then decides: &lt;code&gt;skip&lt;/code&gt;, &lt;code&gt;create&lt;/code&gt;, &lt;code&gt;update&lt;/code&gt;, or &lt;code&gt;flag&lt;/code&gt;. Out-of-scope content never&lt;br&gt;
becomes a wiki page, which means it can never become the basis for an answer.&lt;/p&gt;
&lt;h4 id=&quot;lifecycle-isolation-for-contradicted-pages&quot;&gt;Lifecycle isolation for contradicted pages&lt;/h4&gt;
&lt;p&gt;The scope gate isn't the only place the knowledge base shrinks. When a new source&lt;br&gt;
&lt;em&gt;disagrees&lt;/em&gt; with an existing page, the ingest agent issues a &lt;code&gt;flag&lt;/code&gt; action and the page&lt;br&gt;
moves to a &lt;code&gt;contradicted&lt;/code&gt; lifecycle state, and automatically excluded from retrieval until&lt;br&gt;
the conflict is resolved. This matters because a contradicted page is, by definition,&lt;br&gt;
one where the system can't be confident about what's true. Serving answers from it&lt;br&gt;
would be hallucination with extra steps.&lt;/p&gt;
&lt;p&gt;The contradiction resolver in Synthadoc is an interactive agentic workflow: it surfaces&lt;br&gt;
the conflict, shows a diff of the proposed edit, and requires explicit human approval&lt;br&gt;
before writing any change. The approve-before-write gate is unconditional, and the agent&lt;br&gt;
cannot modify a page without a human seeing the diff first.&lt;/p&gt;
&lt;h3 id=&quot;layer-2-rag-grounding-at-query-time&quot;&gt;Layer 2: RAG Grounding at Query Time&lt;/h3&gt;
&lt;p&gt;When a user asks a question, Synthadoc retrieves the most relevant wiki pages using&lt;br&gt;
hybrid search (BM25 full-text matching with optional vector re-ranking), then builds a&lt;br&gt;
synthesis prompt that includes those pages verbatim:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Answer using ONLY these wiki pages. Cite with [[PageTitle]].
Extract and include all specific facts from the pages — dates, years,
numbers, and names — even when they appear briefly or in passing.
Do not claim a fact is absent unless it is genuinely missing from
every page below.

Question: {question}

Pages:
{retrieved_wiki_pages}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;purpose.md&lt;/code&gt; content is prepended to the pages block as &lt;code&gt;### Wiki Scope&lt;/code&gt;, giving the&lt;br&gt;
model a domain reminder during synthesis as well as at ingest time.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Why this isn't enough on its own:&lt;/strong&gt; A model can still hallucinate &lt;em&gt;within&lt;/em&gt; retrieved&lt;br&gt;
content — misattributing which page said what, inventing a specific number close to but&lt;br&gt;
not the same as a number in the source, or synthesizing a conclusion that no single page&lt;br&gt;
actually states. Grounding reduces the search space; it doesn't eliminate drift.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;When retrieval confidence falls below threshold&lt;/strong&gt;, Synthadoc detects the gap and handles&lt;br&gt;
it transparently rather than hallucinating silently. The immediate response answers from&lt;br&gt;
the model's general knowledge while explicitly noting that the wiki lacks dedicated&lt;br&gt;
coverage — the user always knows they're receiving an unverified answer. At the same time,&lt;br&gt;
Synthadoc generates enrichment suggestions: search queries and known URLs scoped to the&lt;br&gt;
domain in &lt;code&gt;purpose.md&lt;/code&gt;. Retrieved pages flow through the normal ingest pipeline and stay&lt;br&gt;
in &lt;code&gt;draft&lt;/code&gt; state — out of the search corpus — until a human promotes them to &lt;code&gt;active&lt;/code&gt;.&lt;br&gt;
Only then do future queries on that topic get answered from verified wiki content rather&lt;br&gt;
than general knowledge.&lt;/p&gt;
&lt;h3 id=&quot;layer-3-citation-faithfulness-audit&quot;&gt;Layer 3: Citation Faithfulness Audit&lt;/h3&gt;
&lt;p&gt;This is the layer that makes the system &lt;em&gt;verifiable&lt;/em&gt; rather than just hopeful. The citation&lt;br&gt;
faithfulness audit examines each claim in a wiki answer alongside the source lines it cites,&lt;br&gt;
then classifies the relationship:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Verdict         &lt;/th&gt;&lt;th&gt;Meaning&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;supported&lt;/code&gt;     &lt;/td&gt;&lt;td&gt;The claim is directly and accurately supported by the cited source lines.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;drift&lt;/code&gt;         &lt;/td&gt;&lt;td&gt;The claim is related to the source but overstated, simplified, or shifted in meaning.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;hallucination&lt;/code&gt; &lt;/td&gt;&lt;td&gt;The source lines do not support the claim, or directly contradict it.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Each audit is done by a separate LLM call with the claim text and raw source lines&lt;br&gt;
side-by-side. The auditor never sees the original question — only the claim and the lines.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Why a separate LLM pass?&lt;/strong&gt; The same model that wrote the answer is likely to defend&lt;br&gt;
it when asked &quot;is this correct?&quot; Separation of duties matters: the auditor has no stake&lt;br&gt;
in validating the original generation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&quot;what-we-actually-see-in-practice&quot;&gt;What we actually see in practice&lt;/h4&gt;
&lt;p&gt;In our experience, &lt;strong&gt;&lt;code&gt;drift&lt;/code&gt; is the most common finding&lt;/strong&gt; — not outright hallucination.&lt;br&gt;
The model summarizes accurately but slightly overstates a confidence level, rounds a&lt;br&gt;
number, or drops a qualifier (&quot;as of 2023&quot; becomes &quot;currently&quot;). These are small errors&lt;br&gt;
individually but compound across a knowledge base that gets queried repeatedly.&lt;/p&gt;
&lt;p&gt;True &lt;code&gt;hallucination&lt;/code&gt; , where the cited source lines don't support the claim at all ,&lt;br&gt;
is less common in well-grounded RAG, but it happens: specific dates that don't appear&lt;br&gt;
in the source, an attribution switched between two people mentioned in the same paragraph,&lt;br&gt;
a causal relationship inferred from correlation. Those are the findings that matter most,&lt;br&gt;
and the audit surfaces them per-citation so a human knows exactly which source line to&lt;br&gt;
check.&lt;/p&gt;
&lt;p&gt;The audit result is cached per-page with staleness detection. If the page is re-ingested&lt;br&gt;
from updated source material, the cache is invalidated and the next audit reflects the new&lt;br&gt;
content. This surfaces in the web UI as an automatically-loaded faithfulness tab alongside&lt;br&gt;
each wiki page.&lt;/p&gt;
&lt;h3 id=&quot;what-this-doesn-t-solve&quot;&gt;What This Doesn't Solve&lt;/h3&gt;
&lt;p&gt;Honesty matters here too. Two failure modes remain outside what the three layers fully&lt;br&gt;
address:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Synthesis across pages.&lt;/strong&gt; A conclusion drawn by combining facts from multiple pages may&lt;br&gt;
be accurate at each individual step and still misleading in combination. The citation&lt;br&gt;
faithfulness audit operates per-citation, not across synthesized conclusions, so a&lt;br&gt;
cross-page inference that no single page actually states falls outside its scope.&lt;br&gt;
The practical path: when a synthesized answer matters, the cited pages are explicit and&lt;br&gt;
can be verified directly. If the conclusion is important enough to keep, saving it as a&lt;br&gt;
wiki page subjects it to future source ingestion — a new source that disputes it will&lt;br&gt;
trigger the &lt;code&gt;flag&lt;/code&gt; action for human review.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Source quality.&lt;/strong&gt; When a new source &lt;em&gt;disagrees&lt;/em&gt; with an existing page, the &lt;code&gt;flag&lt;/code&gt; action&lt;br&gt;
fires and the contradiction is surfaced. The harder case is when all available sources&lt;br&gt;
agree on the same wrong answer: no flag fires, no contradiction is detected, and Synthadoc&lt;br&gt;
answers confidently from incorrect material. Two mechanisms make this less likely: the&lt;br&gt;
adversarial review in lint, which uses the model's own knowledge to flag claims that&lt;br&gt;
clearly contradict well-established facts; and re-ingesting from updated authoritative&lt;br&gt;
sources, which will trigger the &lt;code&gt;flag&lt;/code&gt; action against any outdated pages. Neither is a&lt;br&gt;
complete solution, but together they make uniform source error less likely to go&lt;br&gt;
unnoticed indefinitely.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;our-view&quot;&gt;Our View&lt;/h3&gt;
&lt;p&gt;The hallucination debate tends to frame the problem as a model capability question: will&lt;br&gt;
the next model be more faithful than the last? We think that's the wrong frame for a&lt;br&gt;
production knowledge system. Even a perfectly faithful model can only be as accurate as&lt;br&gt;
the content it's grounded in, and even a slightly drifting model can be made accountable&lt;br&gt;
if the output is systematically audited.&lt;/p&gt;
&lt;p&gt;Our architectural bet is that &lt;strong&gt;the answer to hallucination isn't a better instruction,&lt;br&gt;
it's a feedback loop&lt;/strong&gt;. Domain scoping controls what enters. Lifecycle states control what&lt;br&gt;
gets queried. Retrieval grounds generation. Citation audit classifies the output. The&lt;br&gt;
contradiction resolver closes the loop: when a conflict is found, a human resolves it, the&lt;br&gt;
page is corrected, and the knowledge base improves. Errors become visible rather than&lt;br&gt;
accumulating silently.&lt;/p&gt;
&lt;p&gt;That's a more defensible position than any number of ALL CAPS instructions.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;try-it&quot;&gt;Try It&lt;/h3&gt;
&lt;p&gt;Synthadoc is open-source. The citation faithfulness audit, contradiction resolver, and&lt;br&gt;
lifecycle system described here are all in the main branch.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;→ &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/axoviq-ai/synthadoc&quot;&gt;github.com/axoviq-ai/synthadoc&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/25713/how-synthadoc-handles-hallucination-and-why-dont-hallucinate-isnt-enough</guid>
<pubDate>Fri, 28 Aug 2026 17:18:42 +0000</pubDate>
</item>
<item>
<title>Snapshots, Diffs, and Rollback: The Audit Trail Every Living Wiki Needs</title>
<link>https://coderlegion.com/24310/snapshots-diffs-and-rollback-the-audit-trail-every-living-wiki-needs</link>
<description>&lt;hr&gt;
&lt;p&gt;There's a class of bug that only shows up in knowledge management systems: the invisible overwrite. You open a wiki page in your editor, make a small tweak to clarify a sentence, and save. Three weeks later a query that used to return the right answer comes back empty. The page is active. The source file hasn't changed. Everything looks fine from the outside. But somewhere in that &quot;small tweak,&quot; you deleted a paragraph that the search index depended on.&lt;/p&gt;
&lt;p&gt;You have no diff. No snapshot. No way back except your memory of what it used to say, which is already suspect, because if it were reliable you would have caught the problem sooner.&lt;/p&gt;
&lt;p&gt;This is the problem we built Content Snapshots to solve in &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/axoviq-ai/synthadoc/releases/tag/v1.2.0&quot;&gt;Synthadoc v1.2.0&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;why-a-lifecycle-system-isn-t-enough-on-its-own&quot;&gt;Why a Lifecycle System Isn't Enough on Its Own&lt;/h3&gt;
&lt;p&gt;Synthadoc already had a detailed audit trail before v1.2.0. Every lifecycle transition — draft to active, active to stale, active to archived — was recorded in the &lt;code&gt;lifecycle_events&lt;/code&gt; table with a timestamp, a trigger source, a reason string, and the before/after states. You could reconstruct &lt;em&gt;when&lt;/em&gt; every state change happened and &lt;em&gt;what caused it&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;What the audit log couldn't answer was: what did the page &lt;em&gt;say&lt;/em&gt; at that moment?&lt;/p&gt;
&lt;p&gt;The distinction matters more than it sounds. A wiki page doesn't just have a state; it has content. And the content can change independently of the state. You can promote a page from draft to active on Monday, edit it in Obsidian on Tuesday, and have the lint agent mark it stale on Wednesday, but if the Tuesday edit introduced a subtle error, the Wednesday stale flag won't tell you anything about what went wrong. You need a record of what the page contained at each of those moments.&lt;/p&gt;
&lt;p&gt;The audit infrastructure was already there — the &lt;code&gt;lifecycle_events&lt;/code&gt; table, the &lt;code&gt;TriggerSource&lt;/code&gt; enum, the log queries. What we were missing was a &lt;code&gt;content_snapshot&lt;/code&gt; column to store the page body alongside each event. That was the architectural gap.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;architecture-three-triggers-one-table&quot;&gt;Architecture: Three Triggers, One Table&lt;/h3&gt;
&lt;p&gt;The design decision we landed on was to capture snapshots at the points in the lifecycle where content is most likely to have meaning: immediately before or at the moment of a consequential event. Not on every save, not on a timer, but at the moments that already have semantic weight in the audit trail.&lt;/p&gt;
&lt;p&gt;Three distinct triggers produce snapshots:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Manual lifecycle transitions.&lt;/strong&gt; Whenever the user calls &lt;code&gt;lifecycle activate&lt;/code&gt;, &lt;code&gt;lifecycle archive&lt;/code&gt;, or &lt;code&gt;lifecycle restore&lt;/code&gt; — through the CLI, the Obsidian Lifecycle modal, the REST API, or the MCP tool — the current page body is captured into the event record. This gives you a snapshot of exactly what the page contained when you decided it was ready to go live, or when you chose to take it offline.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lint-driven state changes.&lt;/strong&gt; When the lint agent transitions a page automatically — promoting a draft to active, marking an active page stale because its source hash changed, auto-resolving a contradiction — the snapshot fires there too. For the auto-resolve case in particular, this captures the post-resolution body that was just written, which is the version you'd want to roll back to if the resolution turns out to be wrong.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Direct edits in Obsidian.&lt;/strong&gt; When you edit a wiki page directly in the Obsidian vault, the plugin's file watcher fires after a two-second debounce. This trigger is the only one with deduplication: &lt;code&gt;snapshot_if_changed()&lt;/code&gt; compares the incoming content to the last stored snapshot, and only writes a new row when the content actually differs. Since the file watcher fires on every keystroke-batch, without dedup you'd generate hundreds of identical rows for a single editing session.&lt;/p&gt;
&lt;p&gt;The state flow looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=5105755032237348805&quot; alt=&quot;architecture&quot;&gt;&lt;/p&gt;
&lt;p&gt;For the Obsidian edit trigger, &lt;code&gt;from_state&lt;/code&gt; and &lt;code&gt;to_state&lt;/code&gt; are both set to the current page state, since no lifecycle transition is occurring. The snapshot is a pure content checkpoint.&lt;/p&gt;
&lt;p&gt;Storage is roughly 3 KB per snapshot at typical wiki page sizes. The existing &lt;code&gt;purge_lifecycle_events()&lt;/code&gt; endpoint handles cleanup, the &lt;code&gt;--before&lt;/code&gt; and &lt;code&gt;--keep-latest&lt;/code&gt; options remove event rows along with their embedded snapshots.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-was-harder-than-expected&quot;&gt;What Was Harder Than Expected&lt;/h3&gt;
&lt;p&gt;The three-trigger model looks clean in the design doc. Getting the semantics right in the implementation was less clean.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The rollback problem.&lt;/strong&gt; Rollback sounds simple: take the page body from snapshot N and write it to disk. But what if you roll back to a snapshot that was itself wrong, and need to undo the rollback? We made rollback undoable by always recording the current body as a new snapshot before writing the target. That means index 1 is always the most recent snapshot, and rolling back to index 2 creates a new index 1 that captures what you just had. The operation never changes &lt;code&gt;page_states&lt;/code&gt; or calls &lt;code&gt;set_page_state&lt;/code&gt; , it's a content operation, not a lifecycle operation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The dedup boundary.&lt;/strong&gt; The two-second debounce on the Obsidian file watcher prevents the snapshot log from filling up with per-keystroke noise. But two seconds isn't always enough if the user pauses mid-edit. We added a content equality check on top of the debounce — &lt;code&gt;snapshot_if_changed()&lt;/code&gt; — so even if multiple debounced events fire for the same editing session, only one row gets written if the content hasn't changed between fires. The check is a simple string equality comparison; at typical page sizes this is negligible overhead.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The ingest carve-out.&lt;/strong&gt; One decision that caused more discussion than expected: ingest-agent transitions do &lt;em&gt;not&lt;/em&gt; produce snapshots. This is intentional. The ingest agent writes the page body from scratch based on the source document; capturing a snapshot at that point would just duplicate the ingested content. The meaningful history is what humans wrote or edited, not what the ingest agent synthesised. Lint-driven transitions — where the agent may overwrite human edits to resolve a contradiction — do snapshot, because that's the case where you might want to go back.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;use-case-walkthrough&quot;&gt;Use Case Walkthrough&lt;/h3&gt;
&lt;p&gt;Here's the scenario this was designed for. You have an active wiki page on a topic that your team references frequently. A colleague opens the page in Obsidian, adds a paragraph, and saves. The edit looks fine. Two days later the lint agent runs, sees that the source file hash has changed, and marks the page stale.&lt;/p&gt;
&lt;p&gt;Before v1.2.0, you'd have an audit trail entry saying &quot;stale, reason: source hash mismatch&quot; but no way to see what the page contained when the colleague edited it versus what it contained when it went stale. You'd have to diff the current file against the source document by hand.&lt;/p&gt;
&lt;p&gt;Now the audit history for that page shows three entries: the original activation (with the body as it was promoted), the colleague's Obsidian edit (captured by &lt;code&gt;snapshot_if_changed()&lt;/code&gt;, a content-only checkpoint), and the lint-driven stale transition (captured with the post-lint body). You can open a diff between any two of them in the Obsidian Content Snapshots tab and immediately see what changed.&lt;/p&gt;
&lt;p&gt;If the colleague's edit inadvertently removed something important, you can roll back to the activation snapshot. The rollback records the current body first, so if you change your mind, rolling back to index 1 gives you back the stale version.&lt;/p&gt;
&lt;h3 id=&quot;section&quot;&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=11170346609771903688&quot; alt=&quot;snapshot mgmt&quot;&gt;&lt;/h3&gt;
&lt;h3 id=&quot;cli-commands&quot;&gt;CLI Commands&lt;/h3&gt;
&lt;p&gt;Content Snapshots are accessible from the command line without the Obsidian plugin.&lt;/p&gt;
&lt;p&gt;List all snapshots for a page, with index, timestamp, transition, and content size:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;synthadoc lifecycle history alan-turing
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;View the content of a specific snapshot (index 1 is always the newest):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;synthadoc lifecycle history alan-turing --index 2 --show-content
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Roll back to a previous snapshot. The current body is saved as a new snapshot first, so the rollback itself is undoable:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;synthadoc lifecycle rollback alan-turing --index 2 --reason &quot;reverting bad edit&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Remove old snapshot data while keeping the most recent N events per page:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;synthadoc audit lifecycle purge --keep-latest 5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Diff between snapshots is available in the Obsidian &lt;strong&gt;Content Snapshots&lt;/strong&gt; tab, select any two entries in the history list to compare them side by side. The CLI surfaces the raw snapshot content via &lt;code&gt;--show-content&lt;/code&gt;; piping two snapshots through your preferred diff tool achieves the same result.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=15340906040811199373&quot; alt=&quot;snapshot diff&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;why-this-matters&quot;&gt;Why This Matters&lt;/h3&gt;
&lt;p&gt;The content snapshot architecture is, at its core, a discipline about what you need to know when things go wrong. Most knowledge management systems are optimised for retrieval — for answering &quot;what does this page say now?&quot; Content Snapshots shifts some of that engineering toward answering &quot;what did it say before?&quot; and &quot;which change caused the problem?&quot;&lt;/p&gt;
&lt;p&gt;The practical consequence is that a wiki built with Synthadoc behaves more like a version-controlled document than a database record. You get the full lifecycle audit — who promoted what and when — plus a point-in-time content record at every moment that mattered. You can reconstruct the editorial history of any page from activation to the present, view any snapshot's content, and roll back to any of them.&lt;/p&gt;
&lt;p&gt;What we found in building this is that the rollback undoability was the right call even though it slightly complicates the storage model. A rollback that can't itself be undone is a footgun. The cost is an extra row per rollback operation; the benefit is that the snapshot log is always accurate and reversible.&lt;/p&gt;
&lt;p&gt;Synthadoc v1.2.0 is available at &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/axoviq-ai/synthadoc&quot;&gt;github.com/axoviq-ai/synthadoc&lt;/a&gt;. The Content Snapshots feature works across all Synthadoc interfaces — CLI, Obsidian plugin, REST API, and MCP.&lt;/p&gt;
&lt;p&gt;For a step-by-step walkthrough — including the Obsidian diff view and a live rollback — see the &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/axoviq-ai/synthadoc/blob/main/docs/user-quick-start-guide.md#snapshots-and-content-recovery&quot;&gt;Snapshots and content recovery&lt;/a&gt; section of the Quick Start Guide.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/24310/snapshots-diffs-and-rollback-the-audit-trail-every-living-wiki-needs</guid>
<pubDate>Sun, 09 Aug 2026 03:47:33 +0000</pubDate>
</item>
</channel>
</rss>