docs: rewrite arborist-one-pager + arborist-two-pager for external readers — drop ticket refs, schema versions, and internal vocabulary; reframe around user value (verified answers, fabricated-citation prevention, replay)

Old drafts read as internal substrate notes. Rewrites lead with what the system does for a consumer or evaluator and what it costs to run, with no references to internal tickets, table names, schema-version strings, governance hash dimensions, or per-record audit-mode tokens. Appendix diagrams updated in lockstep: friendly labels ("grounded / partly grounded / not grounded") replace the schema-column trichotomy, layer names paraphrased away from SURFACE/CORE/PROVIDENCE.
This commit is contained in:
russell@unturf.com 2026-05-14 10:02:08 -04:00
parent 0aada29a8e
commit 75ae470581
No known key found for this signature in database
8 changed files with 296 additions and 326 deletions

View file

@ -1,64 +1,61 @@
arborist — a Q&A store with cryptographically verified answers
================================================================
arborist — answers your AI can prove
=====================================
.. class:: center
*one-page summary · 2026-05-14 · AGPL-3.0-only · permacomputer.com*
*one-page summary · permacomputer.com · AGPL-3.0*
----
Most retrieval-augmented systems hand a language model some context, ask
for an answer, and ship whatever comes back. There is no
machine-checkable claim about what the answer actually quotes from its
source. **arborist inverts that.** Every answer is verified against its
source *after* generation by a deterministic, lexical check that produces
three labels — **STRICT** (every claim verifies), **HYBRID** (mixed),
**UNGROUNDED** (none). The verifier is binary. It does not negotiate.
Answers and labels are written to a content-addressed SQLite cache, every
state change appends a SHA-256 audit-chain row, and a single tampered
record breaks the chain.
Most retrieval-augmented question-answering systems hand a language
model some context and ship whatever the model says. There is no way to
tell whether the answer faithfully reflects the source or whether the
model embroidered it. **arborist closes that gap.** Every answer is
verified against its source *after* the language model finishes, by a
mechanical checker — not by another AI grading the first one. The
checker labels each answer **grounded** (every claim was found in the
source), **partly grounded** (some claims, not all), or **not
grounded**. The label travels with the answer and is stored in a
tamper-evident chain back to the source bytes.
**Three Merkle-bound layers.** Surface (ingested documents — Wikipedia,
HTML, TeX, git, claim-packs), Core (distilled documents bound to their
surfaces by per-chunk inclusion proofs), Providence cache (verified Q&A
keyed on an 8-dimensional invariant that folds in the source-corpus root,
the question hash, the model identity, the governance policy, the schema
version, and three normalization-version dimensions). Bumping any one of
the eight dimensions invalidates prior records on lookup. Cores never
evict.
**Fabricated citations become impossible.** When the model answers, it
never types the quoted text. arborist tags each candidate source
chunk with a short label — ``E1``, ``E2`` — and asks the model to
answer using those labels. The model might write *"Jupiter is the
largest planet [E1], with a radius of about 70 000 km [E2]"*; arborist
renders the actual chunk text at display time. A model cannot fabricate
a quote it never types.
**Synthetic-elision-by-construction-impossible.** In claim-lattice-pointer
mode, the model never types the quote string. The runtime hands it
tokens — ``[E1]``, ``[E2]`` — and interpolates the literal source span at
render time. The model proposes pointer-tagged clauses, the runtime
composes the answer. A class of hallucinations (the frankenquote, the
recombined-token claim) becomes impossible because the model cannot type
a string it never sees.
**The proof path is cheap and mechanical.** Verification is text
comparison, not embeddings, not similarity, not another model. It runs
on a laptop. Optional smart-ranking layers — cross-encoder rerankers,
entailment models — exist on the side; they help arborist find better
evidence, they do not influence whether an answer is certified.
**Soft signals never enter the proof path.** Deflection, coherence,
title-relevance, NLI vetoes, cross-encoder rerankers — all live as
advisory sidecars or as demote-only governance signals folded into the
cache key. They influence what the substrate caches; they do not
influence what it certifies. The hard channel (SHA-256 commitments,
audit chain, proof-of-inclusion) and the soft channel (embeddings,
similarity scores, lexical heuristics) evolve on independent timelines.
**Same question, same document, same answer.** Answers are content-
addressed: the cache key folds in the document, the question, the
model identity, and the policy under which the answer was checked.
Two users asking the same question of the same document under the same
policy hit the same record. Reproducible. Replayable. Shareable.
**What it measures on real traffic.**
**What it has measured on real traffic.**
- **100% mis-cite catch at 0% real-STRICT false positive** on the
808-cell pooled bench-qa run (relevance-reranker veto,
BAAI/bge-reranker-large, n=20 mis-cite fixtures; ticket #000052 §3.2).
- **92 / 92 (100%) warrant-chain coverage** on the claim-pack corpus —
18 public-domain or open-licensed textbook substrates plus curated
alias chains (ticket #000031).
- **0.54 STRICT-rate** on Hermes-3-Llama-3.1-8B quote mode (n=3 × 71
questions, real Wikipedia haystack); lattice modes carry 99%
directive-coverage on the seven-point program.
- **100% misattribution catch at 0% false positives** — every answer
where the cited source was unrelated to the claim was flagged,
without a single grounded answer wrongly demoted, across the full
pooled test bed.
- **5565% topic-deflection catch at 00.4% false positives** — picks
off-topic answers out of the stream while leaving on-topic answers
untouched.
- **100% citation coverage on the curated textbook corpus** — every
cited claim resolves to a chain of evidence ending at a public-domain
or open-licensed source.
**What it runs on.** Python 3.12, SQLite (FTS5 + JSON), optional
cross-encoder rerankers for the soft channel. No GPU required for the
proof path. AGPL-3.0-only. Reference inference endpoint:
**What it costs.** Python 3.12. SQLite, one file (~2 GB for a
Wikipedia-sized corpus). No GPU for the proof path. Use any
OpenAI-compatible inference endpoint; the free reference endpoint is
`hermes.ai.unturf.com <https://hermes.ai.unturf.com>`_. Source:
`git.unturf.com/engineering/unturf/arborist <https://git.unturf.com/engineering/unturf/arborist>`_.
Full whitepaper: `unfirehose.com/merkle-providence-reverse-rag.html
`git.unturf.com/engineering/unturf/arborist
<https://git.unturf.com/engineering/unturf/arborist>`_. Full
whitepaper: `unfirehose.com/merkle-providence-reverse-rag.html
<https://unfirehose.com/merkle-providence-reverse-rag.html>`_.

View file

@ -12,7 +12,7 @@ arborist — Merkle-Providence Reverse RAG, reference implementation
.. class:: center
**License: AGPL-3.0-only** · schema v9.8 · 2026-05-14
**License: AGPL-3.0-only** · 2026-05-14
· *Russell Ballestrini <russell@unturf.com>*
----
@ -20,154 +20,129 @@ arborist — Merkle-Providence Reverse RAG, reference implementation
Abstract
--------
**arborist** is a content-addressed Q&A store with cryptographically
verified answers, the reference runtime for the *Merkle Providence
Reverse RAG* whitepaper (`unfirehose.com/merkle-providence-reverse-rag.html
<https://unfirehose.com/merkle-providence-reverse-rag.html>`_,
Ballestrini · Wong · Morgan, April 2026). Every cached answer carries a Merkle
proof of source origin and a *binary, lexical, no-LLM-in-the-loop*
faithfulness label. The verifier never asks a model to grade itself.
Soft signals — embeddings, NLI vetoes, similarity rerankers — exist
as sidecars and demote-only governance signals; they never enter the
proof path. The reference inference endpoint is Hermes-3-Llama-3.1-8B
served freely at `hermes.ai.unturf.com <https://hermes.ai.unturf.com>`_;
arborist runs against any OpenAI-compatible alternative with a matching
``model_profile_hash``.
**arborist** is an answer-cache for retrieval-augmented question
answering that *verifies every answer against its source* with a
mechanical, deterministic checker — no second AI judging the first.
Every cached answer carries a cryptographic chain back to the bytes
of the source it came from. It is the reference implementation of the
*Merkle Providence Reverse RAG* whitepaper
(`unfirehose.com/merkle-providence-reverse-rag.html
<https://unfirehose.com/merkle-providence-reverse-rag.html>`_), with
a free reference inference endpoint at `hermes.ai.unturf.com
<https://hermes.ai.unturf.com>`_; arborist also runs against any
OpenAI-compatible alternative.
1. The system in one paragraph
-------------------------------
What it solves
--------------
Three SQLite-backed layers, all Merkle-bound. **Surface** holds ingested
documents (Wikipedia dumps, HTML pages, TeX textbooks, git histories,
claim-packs), chunked, hash-rooted, and FTS5-indexed. **Core** holds
distillations bound to their surfaces by per-chunk inclusion proofs in
``derivations.proof_blob``. **Providence cache** holds verified Q&A
records keyed on an 8-dimensional invariant. Every state-changing
operation appends one row to ``audit_events`` carrying
``event_hash = sha256(prev || canonical(body))``; a single tampered row
breaks the chain. Re-ingest is idempotent (same content → same
``document_root`` → no-op); same URI with different content yields a new
doc plus a ``supersedes`` edge (lossless history).
Standard retrieval-augmented systems hand a language model some
context and accept its answer. The user has no way to tell whether the
answer faithfully reflects the source or whether the model improvised.
arborist replaces that trust gap with a check: **every answer is
verified, every answer is labelled, every answer carries a proof chain
back to the bytes of the source it cites.**
2. What makes arborist different
---------------------------------
How it works
------------
Eleven design rules govern the substrate. The four that matter most for
a reviewer:
arborist keeps three layers in a single SQLite file. **Ingested
documents** are split into chunks, hashed, and indexed for full-text
search. **Derived layers** — summaries, indices, distillations — are
hash-bound to the documents they came from. **An answer cache** stores
every verified answer, keyed by the document, the question, the model
that produced it, and the policy under which it was checked. Every
write appends a hash-chained audit row; a single tampered record
breaks the chain.
**The verifier stays binary.** Each evidence unit verifies or does not.
No per-unit confidence scores, no fuzzy-match indicators, no soft
labels. Once a soft signal contaminates the chain, every prior
cryptographic claim becomes negotiable.
When you ask a question, arborist retrieves candidate source chunks,
tags each one with a short label (``E1``, ``E2``, …), and passes them
to the language model. The model emits prose with those labels — for
example, *"Jupiter is the largest planet [E1], with a radius of about
70 000 km [E2]"*. **The model never types the quoted text.** arborist
composes the answer at display time, interpolating the actual chunk
text at each label. A class of fabrication — the synthetic quote that
looks like a verbatim citation but came out of the model's prior —
becomes impossible by construction.
**The runtime owns quote text.** In ``claim_lattice_pointer`` mode the
model emits prose with bracketed pointer tags (``[E1]``, ``[E2]``); the
runtime interpolates the literal source span at render time. The model
never types the quote string. *Synthetic-elision-by-construction-impossible*
— a model cannot produce a frankenquote it cannot type.
After generation, the verifier runs. Four mechanical strategies
(exact-quote match, line-span match, named-entity proximity,
paraphrase coverage) try in sequence to confirm each claim against
the cited chunks. The output is a single label per answer: **grounded**
(every claim verifies), **partly grounded**, or **not grounded**.
There is no "high confidence" middle ground — confidence labels are
soft signals in disguise. The verifier names what it can prove and
what it cannot.
**Soft hash never enters the proof path.** Embeddings, TF-IDF scores,
cross-encoder rerankers, NLI judgments shape ranking and retrieval and
inform demote-only governance. They never feed ``cache_key`` ,
``document_root``, or ``audit_event_hash``. The hard channel and the
soft channel evolve on independent timelines.
What makes it different
-----------------------
**Labels name properties, not vibes.** ``POINTER-LINKED``,
``ANCHOR-WARRANTED``, ``EVIDENCE-WARRANTED``, ``UNGROUNDED`` each name a
property the verifier could lexically confirm or could not. There are
no "high confidence" labels. A reviewer reads the label and immediately
knows what was checked.
**The verifier is mechanical, not neural.** Faithfulness is a textual
property of (answer, evidence, source) and is computed deterministically.
No language model in the proof path means no model drift in the proof
path.
3. The 8-dimensional cache key
-------------------------------
**The model cannot fabricate citations.** arborist sits between the
model's output and the rendered answer; the model proposes references,
arborist renders the content of those references from the actual
source bytes.
A cache hit returns a previously computed answer with a Merkle proof of
origin; a cache miss triggers fresh inference. The key folds together:
**Soft signals stay outside the proof.** Embedding similarity,
cross-encoder rerankers, natural-language-inference judgments — all
useful, all used to find and rank evidence; none of them allowed to
influence the verification label or the audit chain. The cheap,
provable path ships by default; the expensive, semantic path is
opt-in and additive.
================================ ===================================================
``source_root`` Merkle root of the corpus the answer is about
``question_hash`` dedup-mode-aware hash of the question (strict | equivalence_class)
``model_profile_hash`` model identity (name + sampling profile)
``conversation_hash`` prior-turn dependency, if any
``governance_policy_hash`` every policy field that alters generation
``schema_version`` ``v9.8.0``
``canonicalization_version`` ``norm-v1``
``chunking_version`` ``tok-512-v1``
================================ ===================================================
**Replay is free and exact.** Answers are content-addressed. The same
question on the same document under the same policy returns the same
record, with the same proof chain. Different document, different
model, or different policy yields a different cache entry. The cache
partitions cleanly; the corpus stays untouched.
Bumping any one dimension invalidates prior records on lookup —
governance evolves, the corpus does not get rewritten when governance
evolves. Every preflight policy decision is *bound twice*: once into
``governance_policy_hash`` (cache identity) and once into ``run_dag_root``
via the preflight stage (audit replay).
4. The layered verifier
------------------------
Four strategies are tried in order; first to find evidence classifies.
**quote** (sequential pair-matching, never regex — prevents phantom
inter-pair captures), **span** (verbatim line match), **entity**
(proximity-clustered proper nouns), **paraphrase** (token-coverage,
prose-shaped only). Trichotomy on the schema column: STRICT = every unit
verifies, HYBRID = mixed, UNGROUNDED = none. The renderer maps that
column to a four-rung claim-lattice ladder
(POINTER-LINKED → ANCHOR-WARRANTED → EVIDENCE-WARRANTED;
ENTAILMENT-VERIFIED reserved); programmatic callers see the trichotomy,
human-facing surfaces see the ladder. The ENTAILMENT-VERIFIED rung is
deliberately empty — committing NLI to the proof path would re-introduce
the soft/hard boundary leak the architecture is built to prevent.
Honesty about the ceiling is a feature.
----
5. What we measure on real traffic
-----------------------------------
The bench is the scoreboard; the live fixtures are the gates. Numbers
that survive the 5pp signal floor on real-shard, real-LLM runs:
- **Mis-cite catch (claim about X cited to source about Y).** The
``BAAI/bge-reranker-large`` veto at threshold ``θ ≤ -2.42`` catches
20/20 (100%) of mis-cite fixtures at strictly 0/808 (0%) real-STRICT
false positive on the pooled bench-qa, with a 1.4-point margin
between the worst mis-cite and the best STRICT (#000052 §3.2).
- **Deflection catch (off-topic answer to an on-topic question).** Same
reranker catches 11/20 (55%) of real-haystack deflection fixtures at
the same 0/808 FP threshold. The cost-pick alternate
(``ms-marco-MiniLM-L-6-v2``, 5× smaller) catches 13/20 (65%) at 0.4% FP.
- **Recombination catch (right tokens, wrong subject).** The NLI veto
(``facebook/bart-large-mnli``, top-k=12, θc=0.999) catches ~48% of
hand-crafted real-haystack recombination fixtures at 0/808 FP; the
ceiling is selector-bound, not entailment-bound (#000049 §7 #27).
- **Warrant-chain coverage.** 92/92 (100%) of the claim-pack corpus
resolves to a warrant chain (18 public-domain or open-licensed
textbook substrates + curated citation aliases + term aliases;
ticket #000031).
- **STRICT-rate, quote mode, real Wikipedia haystack.** 0.54 on
Hermes-3-Llama-3.1-8B (n=3 × 71 questions, sample-shuffled @ c=4;
see ``docs/qa-modes-bench.md``).
Sample sizes are stated. Synthetic shortcuts are not. The five-point
sweep-wide/sweep-deep discipline ("real-traffic-sized samples or it
didn't happen") lives in ``docs/bench-maxing.md``.
6. Closed substrate work
What you would run it for
-------------------------
Recent substrate work that is *landed and pinned*: ticket #000005
(four-rung ladder display layer), #000008 (broad-quantifier preflight
guard with six-level disable hierarchy), #000010 (metacognition
preflight — false-premise, contradiction, stale-risk, out-of-corpus),
#000015 (π* canonical projections — arithmetic, logic-kernel,
algebra-symbolic), #000027 (canonical-projection persistence in
providence cache), #000031 (claim-pack warrant resolver, 100%
coverage), #000049 §3 (NLI veto, partial closure), #000052 §3.1
(coherence sidecar — circular, vacuous, phrase-component-reuse) and
§3.2 (relevance reranker, viable runtime soft-veto). The synthesis
brief for the independent review thread is
``docs/relevance-and-veto-synthesis-for-dav1d.md``.
You hold a corpus — internal documents, public scientific literature,
a legal archive, a textbook collection — and you want reliable Q&A
over it without paying a closed-source vendor or wiring up a vector
database. You care that the answers track your sources. You want users
to be able to verify, replay, and share the proof. You want it to keep
working when the model changes, when the policy changes, when the
corpus version-bumps.
----
What it costs
-------------
================== ============================================================
Language Python 3.12
Storage SQLite — one file, ~2 GB for a Wikipedia-sized corpus
Compute No GPU required for the proof path. Inference runs wherever
you want — locally, or via an OpenAI-compatible endpoint.
Optional Cross-encoder rerankers (80560 MB) for harder relevance
ranking. They never enter the proof.
License AGPL-3.0-only
================== ============================================================
What it has measured on real traffic
-------------------------------------
- **Misattribution detection.** Catches 100% of answers where the cited
source is unrelated to the claim — at strictly zero false positives
across the full pooled test bed (cross-encoder reranker, hard
threshold, real Wikipedia-haystack questions).
- **Topic-deflection detection.** Catches 5565% of off-topic answers
at 00.4% false positives, depending on the reranker model chosen
(a small 80 MB model and a larger 560 MB model both clear the bar).
- **Citation coverage.** On the curated textbook corpus, 100% of cited
claims resolve to a chain of evidence ending at a public-domain or
open-licensed source — eighteen textbooks plus curated cross-citation
aliases.
Sample sizes are stated, sample sources are real, and the gates are
reported against the same numbers an external reviewer can reproduce
from the repository.
License — Permacomputer Preamble
---------------------------------
@ -188,24 +163,25 @@ License — Permacomputer Preamble
----
Appendix A — Three-layer architecture
--------------------------------------
Appendix A — The three layers
------------------------------
.. figure:: diagrams/pager-arch-stack.png
:width: 5.5in
:align: center
Surface, Core, Providence cache — each layer hash-binds to the next;
every state change appends a row to the audit chain.
Ingested documents, derived layers, and the answer cache — each
layer hash-binds to the next. Every state change appends one row
to the audit chain; a single tampered record breaks the chain.
Appendix B — Verifier flow
---------------------------
Appendix B — How an answer is verified
---------------------------------------
.. figure:: diagrams/pager-verifier-flow.png
:width: 6.5in
:align: center
Question → retrieval → pointer-tagged context → LLM → binary
verifier → trichotomy label → audit chain & providence cache. The
LLM never types the quote string.
The model sees labelled source chunks and emits prose tagged with
those labels — it never types the quote text. A mechanical
verifier then checks every cited claim against the actual source
and assigns the answer one of three labels.

View file

@ -1,4 +1,5 @@
// Compact 3-layer architecture for the 2-pager appendix.
// External-facing labels — no schema-column or table-name vocabulary.
// Render: dot -Tsvg pager-arch-stack.dot -o pager-arch-stack.svg
digraph arch_stack {
@ -9,11 +10,11 @@ digraph arch_stack {
edge [fontname="Helvetica", fontsize=9]
bgcolor="white"
surface [label="SURFACE — ingested documents\nWikipedia · HTML · TeX · git · claim-packs\nchunked → Merkle-rooted → FTS5-indexed", fillcolor="#e9f3ff"]
core [label="CORE — distilled documents\nMerkle-bound to surfaces by per-chunk\ninclusion proofs (derivations.proof_blob)", fillcolor="#dff5e7"]
cache [label="PROVIDENCE CACHE — verified Q&A\n8-dim cache_key · audit_mode label ∈\n{STRICT, HYBRID, UNGROUNDED}", fillcolor="#fff3d6"]
surface [label="Ingested documents\n(Wikipedia · HTML · TeX · git · claim-packs)\nchunked → hashed → full-text-indexed", fillcolor="#e9f3ff"]
core [label="Derived layers\n(summaries, indices, distillations)\nhash-bound back to source chunks", fillcolor="#dff5e7"]
cache [label="Answer cache\nverified Q&A · grounded / partly / not grounded\ncryptographic chain back to source bytes", fillcolor="#fff3d6"]
audit [label="AUDIT CHAIN — every state change\nappends sha256(prev || canonical(body))\nto audit_events (single SQLite file)", fillcolor="#fde7e9", shape=note]
audit [label="Audit chain\nevery write appends a hash-linked row;\na single tampered record breaks the chain", fillcolor="#fde7e9", shape=note]
surface -> core [label="distill", fontsize=9]
core -> cache [label="verify → cache", fontsize=9]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Before After
Before After

View file

@ -4,77 +4,77 @@
<!-- Generated by graphviz version 2.43.0 (0)
-->
<!-- Title: arch_stack Pages: 1 -->
<svg width="379pt" height="268pt"
viewBox="0.00 0.00 379.00 268.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<svg width="434pt" height="268pt"
viewBox="0.00 0.00 433.50 268.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 264)">
<title>arch_stack</title>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-264 375,-264 375,4 -4,4"/>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-264 429.5,-264 429.5,4 -4,4"/>
<!-- surface -->
<g id="node1" class="node">
<title>surface</title>
<path fill="#e9f3ff" stroke="black" d="M319,-44C319,-44 98,-44 98,-44 92,-44 86,-38 86,-32 86,-32 86,-12 86,-12 86,-6 92,0 98,0 98,0 319,0 319,0 325,0 331,-6 331,-12 331,-12 331,-32 331,-32 331,-38 325,-44 319,-44"/>
<text text-anchor="middle" x="208.5" y="-31.2" font-family="Helvetica,sans-Serif" font-size="11.00">SURFACE — ingested documents</text>
<text text-anchor="middle" x="208.5" y="-19.2" font-family="Helvetica,sans-Serif" font-size="11.00">Wikipedia · HTML · TeX · git · claim&#45;packs</text>
<text text-anchor="middle" x="208.5" y="-7.2" font-family="Helvetica,sans-Serif" font-size="11.00">chunked → Merkle&#45;rooted → FTS5&#45;indexed</text>
<path fill="#e9f3ff" stroke="black" d="M380.5,-44C380.5,-44 153.5,-44 153.5,-44 147.5,-44 141.5,-38 141.5,-32 141.5,-32 141.5,-12 141.5,-12 141.5,-6 147.5,0 153.5,0 153.5,0 380.5,0 380.5,0 386.5,0 392.5,-6 392.5,-12 392.5,-12 392.5,-32 392.5,-32 392.5,-38 386.5,-44 380.5,-44"/>
<text text-anchor="middle" x="267" y="-31.2" font-family="Helvetica,sans-Serif" font-size="11.00">Ingested documents</text>
<text text-anchor="middle" x="267" y="-19.2" font-family="Helvetica,sans-Serif" font-size="11.00">(Wikipedia · HTML · TeX · git · claim&#45;packs)</text>
<text text-anchor="middle" x="267" y="-7.2" font-family="Helvetica,sans-Serif" font-size="11.00">chunked → hashed → full&#45;text&#45;indexed</text>
</g>
<!-- core -->
<g id="node2" class="node">
<title>core</title>
<path fill="#dff5e7" stroke="black" d="M245.5,-116C245.5,-116 31.5,-116 31.5,-116 25.5,-116 19.5,-110 19.5,-104 19.5,-104 19.5,-84 19.5,-84 19.5,-78 25.5,-72 31.5,-72 31.5,-72 245.5,-72 245.5,-72 251.5,-72 257.5,-78 257.5,-84 257.5,-84 257.5,-104 257.5,-104 257.5,-110 251.5,-116 245.5,-116"/>
<text text-anchor="middle" x="138.5" y="-103.2" font-family="Helvetica,sans-Serif" font-size="11.00">CORE — distilled documents</text>
<text text-anchor="middle" x="138.5" y="-91.2" font-family="Helvetica,sans-Serif" font-size="11.00">Merkle&#45;bound to surfaces by per&#45;chunk</text>
<text text-anchor="middle" x="138.5" y="-79.2" font-family="Helvetica,sans-Serif" font-size="11.00">inclusion proofs (derivations.proof_blob)</text>
<path fill="#dff5e7" stroke="black" d="M295.5,-116C295.5,-116 112.5,-116 112.5,-116 106.5,-116 100.5,-110 100.5,-104 100.5,-104 100.5,-84 100.5,-84 100.5,-78 106.5,-72 112.5,-72 112.5,-72 295.5,-72 295.5,-72 301.5,-72 307.5,-78 307.5,-84 307.5,-84 307.5,-104 307.5,-104 307.5,-110 301.5,-116 295.5,-116"/>
<text text-anchor="middle" x="204" y="-103.2" font-family="Helvetica,sans-Serif" font-size="11.00">Derived layers</text>
<text text-anchor="middle" x="204" y="-91.2" font-family="Helvetica,sans-Serif" font-size="11.00">(summaries, indices, distillations)</text>
<text text-anchor="middle" x="204" y="-79.2" font-family="Helvetica,sans-Serif" font-size="11.00">hash&#45;bound back to source chunks</text>
</g>
<!-- surface&#45;&gt;core -->
<g id="edge1" class="edge">
<title>surface&#45;&gt;core</title>
<path fill="none" stroke="black" d="M187.5,-44C181.11,-50.39 173.98,-57.52 167.17,-64.33"/>
<polygon fill="black" stroke="black" points="164.57,-61.98 159.97,-71.53 169.52,-66.93 164.57,-61.98"/>
<text text-anchor="middle" x="190.5" y="-55.8" font-family="Helvetica,sans-Serif" font-size="9.00">distill</text>
<path fill="none" stroke="black" d="M248.1,-44C242.47,-50.25 236.2,-57.22 230.2,-63.89"/>
<polygon fill="black" stroke="black" points="227.41,-61.75 223.32,-71.53 232.62,-66.44 227.41,-61.75"/>
<text text-anchor="middle" x="252" y="-55.8" font-family="Helvetica,sans-Serif" font-size="9.00">distill</text>
</g>
<!-- audit -->
<g id="node4" class="node">
<title>audit</title>
<polygon fill="#fde7e9" stroke="black" points="365,-260 128,-260 128,-216 371,-216 371,-254 365,-260"/>
<polyline fill="none" stroke="black" points="365,-260 365,-254 "/>
<polyline fill="none" stroke="black" points="371,-254 365,-254 "/>
<text text-anchor="middle" x="249.5" y="-247.2" font-family="Helvetica,sans-Serif" font-size="11.00">AUDIT CHAIN — every state change</text>
<text text-anchor="middle" x="249.5" y="-235.2" font-family="Helvetica,sans-Serif" font-size="11.00">appends sha256(prev || canonical(body))</text>
<text text-anchor="middle" x="249.5" y="-223.2" font-family="Helvetica,sans-Serif" font-size="11.00">to audit_events (single SQLite file)</text>
<polygon fill="#fde7e9" stroke="black" points="419.5,-260 174.5,-260 174.5,-216 425.5,-216 425.5,-254 419.5,-260"/>
<polyline fill="none" stroke="black" points="419.5,-260 419.5,-254 "/>
<polyline fill="none" stroke="black" points="425.5,-254 419.5,-254 "/>
<text text-anchor="middle" x="300" y="-247.2" font-family="Helvetica,sans-Serif" font-size="11.00">Audit chain</text>
<text text-anchor="middle" x="300" y="-235.2" font-family="Helvetica,sans-Serif" font-size="11.00">every write appends a hash&#45;linked row;</text>
<text text-anchor="middle" x="300" y="-223.2" font-family="Helvetica,sans-Serif" font-size="11.00">a single tampered record breaks the chain</text>
</g>
<!-- surface&#45;&gt;audit -->
<g id="edge5" class="edge">
<title>surface&#45;&gt;audit</title>
<path fill="none" stroke="#888888" stroke-dasharray="5,2" d="M241.95,-44.24C250.98,-51.8 259.59,-61.18 264.5,-72 270.08,-84.3 269.38,-158.79 260.95,-206.05"/>
<polygon fill="#888888" stroke="#888888" points="257.49,-205.49 258.96,-215.98 264.35,-206.86 257.49,-205.49"/>
<path fill="none" stroke="#888888" stroke-dasharray="5,2" d="M294.75,-44.09C302.85,-51.88 310.68,-61.42 315,-72 320.38,-85.18 319.5,-160.34 311.42,-206.13"/>
<polygon fill="#888888" stroke="#888888" points="307.98,-205.49 309.46,-215.98 314.84,-206.85 307.98,-205.49"/>
</g>
<!-- cache -->
<g id="node3" class="node">
<title>cache</title>
<path fill="#fff3d6" stroke="black" d="M215,-188C215,-188 12,-188 12,-188 6,-188 0,-182 0,-176 0,-176 0,-156 0,-156 0,-150 6,-144 12,-144 12,-144 215,-144 215,-144 221,-144 227,-150 227,-156 227,-156 227,-176 227,-176 227,-182 221,-188 215,-188"/>
<text text-anchor="middle" x="113.5" y="-175.2" font-family="Helvetica,sans-Serif" font-size="11.00">PROVIDENCE CACHE — verified Q&amp;A</text>
<text text-anchor="middle" x="113.5" y="-163.2" font-family="Helvetica,sans-Serif" font-size="11.00">8&#45;dim cache_key · audit_mode label ∈</text>
<text text-anchor="middle" x="113.5" y="-151.2" font-family="Helvetica,sans-Serif" font-size="11.00">{STRICT, HYBRID, UNGROUNDED}</text>
<path fill="#fff3d6" stroke="black" d="M266,-188C266,-188 12,-188 12,-188 6,-188 0,-182 0,-176 0,-176 0,-156 0,-156 0,-150 6,-144 12,-144 12,-144 266,-144 266,-144 272,-144 278,-150 278,-156 278,-156 278,-176 278,-176 278,-182 272,-188 266,-188"/>
<text text-anchor="middle" x="139" y="-175.2" font-family="Helvetica,sans-Serif" font-size="11.00">Answer cache</text>
<text text-anchor="middle" x="139" y="-163.2" font-family="Helvetica,sans-Serif" font-size="11.00">verified Q&amp;A · grounded / partly / not grounded</text>
<text text-anchor="middle" x="139" y="-151.2" font-family="Helvetica,sans-Serif" font-size="11.00">cryptographic chain back to source bytes</text>
</g>
<!-- core&#45;&gt;cache -->
<g id="edge2" class="edge">
<title>core&#45;&gt;cache</title>
<path fill="none" stroke="black" d="M131,-116C129.01,-121.57 126.82,-127.71 124.68,-133.71"/>
<polygon fill="black" stroke="black" points="121.24,-132.93 121.17,-143.53 127.83,-135.29 121.24,-132.93"/>
<text text-anchor="middle" x="160.5" y="-127.8" font-family="Helvetica,sans-Serif" font-size="9.00">verify → cache</text>
<path fill="none" stroke="black" d="M184.5,-116C178.69,-122.25 172.22,-129.22 166.03,-135.89"/>
<polygon fill="black" stroke="black" points="163.18,-133.82 158.94,-143.53 168.31,-138.58 163.18,-133.82"/>
<text text-anchor="middle" x="209" y="-127.8" font-family="Helvetica,sans-Serif" font-size="9.00">verify → cache</text>
</g>
<!-- core&#45;&gt;audit -->
<g id="edge4" class="edge">
<title>core&#45;&gt;audit</title>
<path fill="none" stroke="#888888" stroke-dasharray="5,2" d="M200.73,-116.01C213.64,-123.04 225.91,-132.2 234.5,-144 247.33,-161.63 250.87,-186.11 251.31,-205.54"/>
<polygon fill="#888888" stroke="#888888" points="247.81,-205.72 251.28,-215.73 254.81,-205.74 247.81,-205.72"/>
<path fill="none" stroke="#888888" stroke-dasharray="5,2" d="M254.11,-116.13C265.91,-123.34 277.28,-132.55 285,-144 297.19,-162.08 300.75,-186.55 301.37,-205.85"/>
<polygon fill="#888888" stroke="#888888" points="297.87,-205.99 301.44,-215.97 304.87,-205.94 297.87,-205.99"/>
</g>
<!-- cache&#45;&gt;audit -->
<g id="edge3" class="edge">
<title>cache&#45;&gt;audit</title>
<path fill="none" stroke="#888888" stroke-dasharray="5,2" d="M151.73,-188.09C163.18,-194.29 175.8,-201.01 187.5,-207 190.37,-208.47 193.33,-209.97 196.32,-211.46"/>
<polygon fill="#888888" stroke="#888888" points="194.83,-214.63 205.35,-215.94 197.94,-208.36 194.83,-214.63"/>
<text text-anchor="middle" x="213" y="-199.8" font-family="Helvetica,sans-Serif" font-size="9.00">every write</text>
<path fill="none" stroke="#888888" stroke-dasharray="5,2" d="M187.31,-188C204.58,-195.51 224.22,-204.05 242.21,-211.88"/>
<polygon fill="#888888" stroke="#888888" points="240.88,-215.11 251.45,-215.89 243.68,-208.69 240.88,-215.11"/>
<text text-anchor="middle" x="255.5" y="-199.8" font-family="Helvetica,sans-Serif" font-size="9.00">every write</text>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Before After
Before After

View file

@ -1,5 +1,5 @@
// Compact verifier flow for the 2-pager appendix.
// (Question, source-chunks) → model → (text, pointers) → verifier → label.
// External-facing labels — no internal vocabulary.
// Render: dot -Tsvg pager-verifier-flow.dot -o pager-verifier-flow.svg
digraph verifier_flow {
@ -10,27 +10,27 @@ digraph verifier_flow {
edge [fontname="Helvetica", fontsize=11]
bgcolor="white"
question [label="QUESTION", fillcolor="#fff7e6", shape=note]
retrieval [label="retrieval\n(FTS5 + phrase route\n+ rivalry exclusion)", fillcolor="#e9f3ff"]
pointers [label="POINTER MAP\nE1 → span\nE2 → span\n…", fillcolor="#dff5e7", shape=note]
model [label="LLM\nsees: question +\npointer-tagged chunks\nemits: prose with [E1] [E2] tags\n(never types the quote string)", fillcolor="#f0e6ff"]
verifier [label="verifier\n(quote · span · entity\n· paraphrase — binary,\nno LLM call)", fillcolor="#fff3d6"]
question [label="Question", fillcolor="#fff7e6", shape=note]
retrieval [label="Retrieve\ncandidate source chunks", fillcolor="#e9f3ff"]
pointers [label="Labelled chunks\nE1 → text\nE2 → text\n…", fillcolor="#dff5e7", shape=note]
model [label="Language model\nsees: question + labelled chunks\nemits: prose tagged [E1] [E2]\n(never types the quote text)", fillcolor="#f0e6ff"]
verifier [label="Verifier\nfour mechanical strategies,\nno model in the loop", fillcolor="#fff3d6"]
label_strict [label="STRICT\nevery claim verifies", fillcolor="#28a745", fontcolor="white"]
label_hybrid [label="HYBRID\nsome verify, some don't", fillcolor="#ffc107"]
label_ungrounded [label="UNGROUNDED\nnone verify", fillcolor="#dc3545", fontcolor="white"]
label_grounded [label="grounded\nevery claim verifies", fillcolor="#28a745", fontcolor="white"]
label_partial [label="partly grounded\nsome verify, some don't", fillcolor="#ffc107"]
label_ungrounded [label="not grounded\nnone verify", fillcolor="#dc3545", fontcolor="white"]
audit [label="audit chain\n+ providence cache\n(8-dim key, governance-aware)", fillcolor="#fde7e9", shape=note]
audit [label="Audit chain\n+ answer cache\n(reproducible, replayable)", fillcolor="#fde7e9", shape=note]
question -> retrieval -> pointers
pointers -> model
model -> verifier [label="answer\n+ pointer IDs"]
verifier -> label_strict
verifier -> label_hybrid
model -> verifier [label="answer with [E1] [E2] tags"]
verifier -> label_grounded
verifier -> label_partial
verifier -> label_ungrounded
label_strict -> audit [style=dashed, color="#888888"]
label_hybrid -> audit [style=dashed, color="#888888"]
label_grounded -> audit [style=dashed, color="#888888"]
label_partial -> audit [style=dashed, color="#888888"]
label_ungrounded -> audit [style=dashed, color="#888888"]
{rank=same; label_strict; label_hybrid; label_ungrounded}
{rank=same; label_grounded; label_partial; label_ungrounded}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Before After
Before After

View file

@ -4,149 +4,145 @@
<!-- Generated by graphviz version 2.43.0 (0)
-->
<!-- Title: verifier_flow Pages: 1 -->
<svg width="1288pt" height="152pt"
viewBox="0.00 0.00 1288.00 152.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<svg width="1389pt" height="152pt"
viewBox="0.00 0.00 1389.00 152.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 148)">
<title>verifier_flow</title>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-148 1284,-148 1284,4 -4,4"/>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-148 1385,-148 1385,4 -4,4"/>
<!-- question -->
<g id="node1" class="node">
<title>question</title>
<polygon fill="#fff7e6" stroke="black" points="74,-90 0,-90 0,-54 80,-54 80,-84 74,-90"/>
<polyline fill="none" stroke="black" points="74,-90 74,-84 "/>
<polyline fill="none" stroke="black" points="80,-84 74,-84 "/>
<text text-anchor="middle" x="40" y="-68.9" font-family="Helvetica,sans-Serif" font-size="12.00">QUESTION</text>
<polygon fill="#fff7e6" stroke="black" points="64,-90 0,-90 0,-54 70,-54 70,-84 64,-90"/>
<polyline fill="none" stroke="black" points="64,-90 64,-84 "/>
<polyline fill="none" stroke="black" points="70,-84 64,-84 "/>
<text text-anchor="middle" x="35" y="-68.9" font-family="Helvetica,sans-Serif" font-size="12.00">Question</text>
</g>
<!-- retrieval -->
<g id="node2" class="node">
<title>retrieval</title>
<path fill="#e9f3ff" stroke="black" d="M239,-95.5C239,-95.5 119,-95.5 119,-95.5 113,-95.5 107,-89.5 107,-83.5 107,-83.5 107,-60.5 107,-60.5 107,-54.5 113,-48.5 119,-48.5 119,-48.5 239,-48.5 239,-48.5 245,-48.5 251,-54.5 251,-60.5 251,-60.5 251,-83.5 251,-83.5 251,-89.5 245,-95.5 239,-95.5"/>
<text text-anchor="middle" x="179" y="-81.9" font-family="Helvetica,sans-Serif" font-size="12.00">retrieval</text>
<text text-anchor="middle" x="179" y="-68.9" font-family="Helvetica,sans-Serif" font-size="12.00">(FTS5 + phrase route</text>
<text text-anchor="middle" x="179" y="-55.9" font-family="Helvetica,sans-Serif" font-size="12.00">+ rivalry exclusion)</text>
<path fill="#e9f3ff" stroke="black" d="M250,-90C250,-90 109,-90 109,-90 103,-90 97,-84 97,-78 97,-78 97,-66 97,-66 97,-60 103,-54 109,-54 109,-54 250,-54 250,-54 256,-54 262,-60 262,-66 262,-66 262,-78 262,-78 262,-84 256,-90 250,-90"/>
<text text-anchor="middle" x="179.5" y="-75.4" font-family="Helvetica,sans-Serif" font-size="12.00">Retrieve</text>
<text text-anchor="middle" x="179.5" y="-62.4" font-family="Helvetica,sans-Serif" font-size="12.00">candidate source chunks</text>
</g>
<!-- question&#45;&gt;retrieval -->
<g id="edge1" class="edge">
<title>question&#45;&gt;retrieval</title>
<path fill="none" stroke="black" d="M80.26,-72C85.5,-72 91.02,-72 96.66,-72"/>
<polygon fill="black" stroke="black" points="96.9,-75.5 106.9,-72 96.9,-68.5 96.9,-75.5"/>
<path fill="none" stroke="black" d="M70.13,-72C75.41,-72 81.06,-72 86.91,-72"/>
<polygon fill="black" stroke="black" points="86.91,-75.5 96.91,-72 86.91,-68.5 86.91,-75.5"/>
</g>
<!-- pointers -->
<g id="node3" class="node">
<title>pointers</title>
<polygon fill="#dff5e7" stroke="black" points="372,-102 278,-102 278,-42 378,-42 378,-96 372,-102"/>
<polyline fill="none" stroke="black" points="372,-102 372,-96 "/>
<polyline fill="none" stroke="black" points="378,-96 372,-96 "/>
<text text-anchor="middle" x="328" y="-88.4" font-family="Helvetica,sans-Serif" font-size="12.00">POINTER MAP</text>
<text text-anchor="middle" x="328" y="-75.4" font-family="Helvetica,sans-Serif" font-size="12.00">E1 → span</text>
<text text-anchor="middle" x="328" y="-62.4" font-family="Helvetica,sans-Serif" font-size="12.00">E2 → span</text>
<text text-anchor="middle" x="328" y="-49.4" font-family="Helvetica,sans-Serif" font-size="12.00"></text>
<polygon fill="#dff5e7" stroke="black" points="396,-102 289,-102 289,-42 402,-42 402,-96 396,-102"/>
<polyline fill="none" stroke="black" points="396,-102 396,-96 "/>
<polyline fill="none" stroke="black" points="402,-96 396,-96 "/>
<text text-anchor="middle" x="345.5" y="-88.4" font-family="Helvetica,sans-Serif" font-size="12.00">Labelled chunks</text>
<text text-anchor="middle" x="345.5" y="-75.4" font-family="Helvetica,sans-Serif" font-size="12.00">E1 → text</text>
<text text-anchor="middle" x="345.5" y="-62.4" font-family="Helvetica,sans-Serif" font-size="12.00">E2 → text</text>
<text text-anchor="middle" x="345.5" y="-49.4" font-family="Helvetica,sans-Serif" font-size="12.00"></text>
</g>
<!-- retrieval&#45;&gt;pointers -->
<g id="edge2" class="edge">
<title>retrieval&#45;&gt;pointers</title>
<path fill="none" stroke="black" d="M251.35,-72C256.79,-72 262.24,-72 267.59,-72"/>
<polygon fill="black" stroke="black" points="267.72,-75.5 277.72,-72 267.72,-68.5 267.72,-75.5"/>
<path fill="none" stroke="black" d="M262.02,-72C267.51,-72 273,-72 278.39,-72"/>
<polygon fill="black" stroke="black" points="278.62,-75.5 288.62,-72 278.62,-68.5 278.62,-75.5"/>
</g>
<!-- model -->
<g id="node4" class="node">
<title>model</title>
<path fill="#f0e6ff" stroke="black" d="M596,-108.5C596,-108.5 417,-108.5 417,-108.5 411,-108.5 405,-102.5 405,-96.5 405,-96.5 405,-47.5 405,-47.5 405,-41.5 411,-35.5 417,-35.5 417,-35.5 596,-35.5 596,-35.5 602,-35.5 608,-41.5 608,-47.5 608,-47.5 608,-96.5 608,-96.5 608,-102.5 602,-108.5 596,-108.5"/>
<text text-anchor="middle" x="506.5" y="-94.9" font-family="Helvetica,sans-Serif" font-size="12.00">LLM</text>
<text text-anchor="middle" x="506.5" y="-81.9" font-family="Helvetica,sans-Serif" font-size="12.00">sees: question +</text>
<text text-anchor="middle" x="506.5" y="-68.9" font-family="Helvetica,sans-Serif" font-size="12.00">pointer&#45;tagged chunks</text>
<text text-anchor="middle" x="506.5" y="-55.9" font-family="Helvetica,sans-Serif" font-size="12.00">emits: prose with [E1] [E2] tags</text>
<text text-anchor="middle" x="506.5" y="-42.9" font-family="Helvetica,sans-Serif" font-size="12.00">(never types the quote string)</text>
<path fill="#f0e6ff" stroke="black" d="M629,-102C629,-102 441,-102 441,-102 435,-102 429,-96 429,-90 429,-90 429,-54 429,-54 429,-48 435,-42 441,-42 441,-42 629,-42 629,-42 635,-42 641,-48 641,-54 641,-54 641,-90 641,-90 641,-96 635,-102 629,-102"/>
<text text-anchor="middle" x="535" y="-88.4" font-family="Helvetica,sans-Serif" font-size="12.00">Language model</text>
<text text-anchor="middle" x="535" y="-75.4" font-family="Helvetica,sans-Serif" font-size="12.00">sees: question + labelled chunks</text>
<text text-anchor="middle" x="535" y="-62.4" font-family="Helvetica,sans-Serif" font-size="12.00">emits: prose tagged [E1] [E2]</text>
<text text-anchor="middle" x="535" y="-49.4" font-family="Helvetica,sans-Serif" font-size="12.00">(never types the quote text)</text>
</g>
<!-- pointers&#45;&gt;model -->
<g id="edge3" class="edge">
<title>pointers&#45;&gt;model</title>
<path fill="none" stroke="black" d="M378.17,-72C383.46,-72 388.98,-72 394.64,-72"/>
<polygon fill="black" stroke="black" points="394.95,-75.5 404.95,-72 394.95,-68.5 394.95,-75.5"/>
<path fill="none" stroke="black" d="M402.25,-72C407.58,-72 413.11,-72 418.74,-72"/>
<polygon fill="black" stroke="black" points="418.99,-75.5 428.99,-72 418.99,-68.5 418.99,-75.5"/>
</g>
<!-- verifier -->
<g id="node5" class="node">
<title>verifier</title>
<path fill="#fff3d6" stroke="black" d="M849,-102C849,-102 719,-102 719,-102 713,-102 707,-96 707,-90 707,-90 707,-54 707,-54 707,-48 713,-42 719,-42 719,-42 849,-42 849,-42 855,-42 861,-48 861,-54 861,-54 861,-90 861,-90 861,-96 855,-102 849,-102"/>
<text text-anchor="middle" x="784" y="-88.4" font-family="Helvetica,sans-Serif" font-size="12.00">verifier</text>
<text text-anchor="middle" x="784" y="-75.4" font-family="Helvetica,sans-Serif" font-size="12.00">(quote · span · entity</text>
<text text-anchor="middle" x="784" y="-62.4" font-family="Helvetica,sans-Serif" font-size="12.00">· paraphrase — binary,</text>
<text text-anchor="middle" x="784" y="-49.4" font-family="Helvetica,sans-Serif" font-size="12.00">no LLM call)</text>
<path fill="#fff3d6" stroke="black" d="M984,-95.5C984,-95.5 827,-95.5 827,-95.5 821,-95.5 815,-89.5 815,-83.5 815,-83.5 815,-60.5 815,-60.5 815,-54.5 821,-48.5 827,-48.5 827,-48.5 984,-48.5 984,-48.5 990,-48.5 996,-54.5 996,-60.5 996,-60.5 996,-83.5 996,-83.5 996,-89.5 990,-95.5 984,-95.5"/>
<text text-anchor="middle" x="905.5" y="-81.9" font-family="Helvetica,sans-Serif" font-size="12.00">Verifier</text>
<text text-anchor="middle" x="905.5" y="-68.9" font-family="Helvetica,sans-Serif" font-size="12.00">four mechanical strategies,</text>
<text text-anchor="middle" x="905.5" y="-55.9" font-family="Helvetica,sans-Serif" font-size="12.00">no model in the loop</text>
</g>
<!-- model&#45;&gt;verifier -->
<g id="edge4" class="edge">
<title>model&#45;&gt;verifier</title>
<path fill="none" stroke="black" d="M608.13,-72C637.15,-72 668.55,-72 696.63,-72"/>
<polygon fill="black" stroke="black" points="696.81,-75.5 706.81,-72 696.81,-68.5 696.81,-75.5"/>
<text text-anchor="middle" x="657.5" y="-87.2" font-family="Helvetica,sans-Serif" font-size="11.00">answer</text>
<text text-anchor="middle" x="657.5" y="-75.2" font-family="Helvetica,sans-Serif" font-size="11.00">+ pointer IDs</text>
<path fill="none" stroke="black" d="M641.1,-72C692.45,-72 753.92,-72 804.56,-72"/>
<polygon fill="black" stroke="black" points="804.87,-75.5 814.87,-72 804.87,-68.5 804.87,-75.5"/>
<text text-anchor="middle" x="728" y="-75.2" font-family="Helvetica,sans-Serif" font-size="11.00">answer with [E1] [E2] tags</text>
</g>
<!-- label_strict -->
<!-- label_grounded -->
<g id="node6" class="node">
<title>label_strict</title>
<path fill="#28a745" stroke="black" d="M1023.5,-144C1023.5,-144 913.5,-144 913.5,-144 907.5,-144 901.5,-138 901.5,-132 901.5,-132 901.5,-120 901.5,-120 901.5,-114 907.5,-108 913.5,-108 913.5,-108 1023.5,-108 1023.5,-108 1029.5,-108 1035.5,-114 1035.5,-120 1035.5,-120 1035.5,-132 1035.5,-132 1035.5,-138 1029.5,-144 1023.5,-144"/>
<text text-anchor="middle" x="968.5" y="-129.4" font-family="Helvetica,sans-Serif" font-size="12.00" fill="white">STRICT</text>
<text text-anchor="middle" x="968.5" y="-116.4" font-family="Helvetica,sans-Serif" font-size="12.00" fill="white">every claim verifies</text>
<title>label_grounded</title>
<path fill="#28a745" stroke="black" d="M1158.5,-144C1158.5,-144 1048.5,-144 1048.5,-144 1042.5,-144 1036.5,-138 1036.5,-132 1036.5,-132 1036.5,-120 1036.5,-120 1036.5,-114 1042.5,-108 1048.5,-108 1048.5,-108 1158.5,-108 1158.5,-108 1164.5,-108 1170.5,-114 1170.5,-120 1170.5,-120 1170.5,-132 1170.5,-132 1170.5,-138 1164.5,-144 1158.5,-144"/>
<text text-anchor="middle" x="1103.5" y="-129.4" font-family="Helvetica,sans-Serif" font-size="12.00" fill="white">grounded</text>
<text text-anchor="middle" x="1103.5" y="-116.4" font-family="Helvetica,sans-Serif" font-size="12.00" fill="white">every claim verifies</text>
</g>
<!-- verifier&#45;&gt;label_strict -->
<!-- verifier&#45;&gt;label_grounded -->
<g id="edge5" class="edge">
<title>verifier&#45;&gt;label_strict</title>
<path fill="none" stroke="black" d="M861.07,-94.51C872.92,-98.01 885.11,-101.62 896.83,-105.09"/>
<polygon fill="black" stroke="black" points="895.96,-108.48 906.54,-107.96 897.95,-101.77 895.96,-108.48"/>
<title>verifier&#45;&gt;label_grounded</title>
<path fill="none" stroke="black" d="M991.9,-95.53C1003.75,-98.79 1015.84,-102.13 1027.48,-105.33"/>
<polygon fill="black" stroke="black" points="1026.56,-108.71 1037.13,-107.99 1028.42,-101.96 1026.56,-108.71"/>
</g>
<!-- label_hybrid -->
<!-- label_partial -->
<g id="node7" class="node">
<title>label_hybrid</title>
<path fill="#ffc107" stroke="black" d="M1037,-90C1037,-90 900,-90 900,-90 894,-90 888,-84 888,-78 888,-78 888,-66 888,-66 888,-60 894,-54 900,-54 900,-54 1037,-54 1037,-54 1043,-54 1049,-60 1049,-66 1049,-66 1049,-78 1049,-78 1049,-84 1043,-90 1037,-90"/>
<text text-anchor="middle" x="968.5" y="-75.4" font-family="Helvetica,sans-Serif" font-size="12.00">HYBRID</text>
<text text-anchor="middle" x="968.5" y="-62.4" font-family="Helvetica,sans-Serif" font-size="12.00">some verify, some don&#39;t</text>
<title>label_partial</title>
<path fill="#ffc107" stroke="black" d="M1172,-90C1172,-90 1035,-90 1035,-90 1029,-90 1023,-84 1023,-78 1023,-78 1023,-66 1023,-66 1023,-60 1029,-54 1035,-54 1035,-54 1172,-54 1172,-54 1178,-54 1184,-60 1184,-66 1184,-66 1184,-78 1184,-78 1184,-84 1178,-90 1172,-90"/>
<text text-anchor="middle" x="1103.5" y="-75.4" font-family="Helvetica,sans-Serif" font-size="12.00">partly grounded</text>
<text text-anchor="middle" x="1103.5" y="-62.4" font-family="Helvetica,sans-Serif" font-size="12.00">some verify, some don&#39;t</text>
</g>
<!-- verifier&#45;&gt;label_hybrid -->
<!-- verifier&#45;&gt;label_partial -->
<g id="edge6" class="edge">
<title>verifier&#45;&gt;label_hybrid</title>
<path fill="none" stroke="black" d="M861.07,-72C866.44,-72 871.87,-72 877.31,-72"/>
<polygon fill="black" stroke="black" points="877.72,-75.5 887.72,-72 877.72,-68.5 877.72,-75.5"/>
<title>verifier&#45;&gt;label_partial</title>
<path fill="none" stroke="black" d="M996.18,-72C1001.59,-72 1007.03,-72 1012.43,-72"/>
<polygon fill="black" stroke="black" points="1012.77,-75.5 1022.77,-72 1012.77,-68.5 1012.77,-75.5"/>
</g>
<!-- label_ungrounded -->
<g id="node8" class="node">
<title>label_ungrounded</title>
<path fill="#dc3545" stroke="black" d="M1009,-36C1009,-36 928,-36 928,-36 922,-36 916,-30 916,-24 916,-24 916,-12 916,-12 916,-6 922,0 928,0 928,0 1009,0 1009,0 1015,0 1021,-6 1021,-12 1021,-12 1021,-24 1021,-24 1021,-30 1015,-36 1009,-36"/>
<text text-anchor="middle" x="968.5" y="-21.4" font-family="Helvetica,sans-Serif" font-size="12.00" fill="white">UNGROUNDED</text>
<text text-anchor="middle" x="968.5" y="-8.4" font-family="Helvetica,sans-Serif" font-size="12.00" fill="white">none verify</text>
<path fill="#dc3545" stroke="black" d="M1140,-36C1140,-36 1067,-36 1067,-36 1061,-36 1055,-30 1055,-24 1055,-24 1055,-12 1055,-12 1055,-6 1061,0 1067,0 1067,0 1140,0 1140,0 1146,0 1152,-6 1152,-12 1152,-12 1152,-24 1152,-24 1152,-30 1146,-36 1140,-36"/>
<text text-anchor="middle" x="1103.5" y="-21.4" font-family="Helvetica,sans-Serif" font-size="12.00" fill="white">not grounded</text>
<text text-anchor="middle" x="1103.5" y="-8.4" font-family="Helvetica,sans-Serif" font-size="12.00" fill="white">none verify</text>
</g>
<!-- verifier&#45;&gt;label_ungrounded -->
<g id="edge7" class="edge">
<title>verifier&#45;&gt;label_ungrounded</title>
<path fill="none" stroke="black" d="M861.07,-49.49C876.09,-45.05 891.68,-40.44 906.14,-36.16"/>
<polygon fill="black" stroke="black" points="907.35,-39.45 915.94,-33.25 905.36,-32.74 907.35,-39.45"/>
<path fill="none" stroke="black" d="M991.9,-48.47C1009.75,-43.55 1028.18,-38.48 1044.74,-33.91"/>
<polygon fill="black" stroke="black" points="1046.08,-37.17 1054.79,-31.14 1044.22,-30.43 1046.08,-37.17"/>
</g>
<!-- audit -->
<g id="node9" class="node">
<title>audit</title>
<polygon fill="#fde7e9" stroke="black" points="1274,-95.5 1076,-95.5 1076,-48.5 1280,-48.5 1280,-89.5 1274,-95.5"/>
<polyline fill="none" stroke="black" points="1274,-95.5 1274,-89.5 "/>
<polyline fill="none" stroke="black" points="1280,-89.5 1274,-89.5 "/>
<text text-anchor="middle" x="1178" y="-81.9" font-family="Helvetica,sans-Serif" font-size="12.00">audit chain</text>
<text text-anchor="middle" x="1178" y="-68.9" font-family="Helvetica,sans-Serif" font-size="12.00">+ providence cache</text>
<text text-anchor="middle" x="1178" y="-55.9" font-family="Helvetica,sans-Serif" font-size="12.00">(8&#45;dim key, governance&#45;aware)</text>
<polygon fill="#fde7e9" stroke="black" points="1375,-95.5 1211,-95.5 1211,-48.5 1381,-48.5 1381,-89.5 1375,-95.5"/>
<polyline fill="none" stroke="black" points="1375,-95.5 1375,-89.5 "/>
<polyline fill="none" stroke="black" points="1381,-89.5 1375,-89.5 "/>
<text text-anchor="middle" x="1296" y="-81.9" font-family="Helvetica,sans-Serif" font-size="12.00">Audit chain</text>
<text text-anchor="middle" x="1296" y="-68.9" font-family="Helvetica,sans-Serif" font-size="12.00">+ answer cache</text>
<text text-anchor="middle" x="1296" y="-55.9" font-family="Helvetica,sans-Serif" font-size="12.00">(reproducible, replayable)</text>
</g>
<!-- label_strict&#45;&gt;audit -->
<!-- label_grounded&#45;&gt;audit -->
<g id="edge8" class="edge">
<title>label_strict&#45;&gt;audit</title>
<path fill="none" stroke="#888888" stroke-dasharray="5,2" d="M1035.72,-108.77C1048.7,-105.39 1062.59,-101.77 1076.4,-98.18"/>
<polygon fill="#888888" stroke="#888888" points="1077.63,-101.48 1086.43,-95.57 1075.87,-94.7 1077.63,-101.48"/>
<title>label_grounded&#45;&gt;audit</title>
<path fill="none" stroke="#888888" stroke-dasharray="5,2" d="M1168.2,-107.94C1179.18,-104.83 1190.8,-101.54 1202.37,-98.26"/>
<polygon fill="#888888" stroke="#888888" points="1203.39,-101.61 1212.05,-95.51 1201.48,-94.87 1203.39,-101.61"/>
</g>
<!-- label_hybrid&#45;&gt;audit -->
<!-- label_partial&#45;&gt;audit -->
<g id="edge9" class="edge">
<title>label_hybrid&#45;&gt;audit</title>
<path fill="none" stroke="#888888" stroke-dasharray="5,2" d="M1049.13,-72C1054.63,-72 1060.23,-72 1065.85,-72"/>
<polygon fill="#888888" stroke="#888888" points="1065.99,-75.5 1075.99,-72 1065.99,-68.5 1065.99,-75.5"/>
<title>label_partial&#45;&gt;audit</title>
<path fill="none" stroke="#888888" stroke-dasharray="5,2" d="M1184.19,-72C1189.53,-72 1194.94,-72 1200.35,-72"/>
<polygon fill="#888888" stroke="#888888" points="1200.72,-75.5 1210.72,-72 1200.72,-68.5 1200.72,-75.5"/>
</g>
<!-- label_ungrounded&#45;&gt;audit -->
<g id="edge10" class="edge">
<title>label_ungrounded&#45;&gt;audit</title>
<path fill="none" stroke="#888888" stroke-dasharray="5,2" d="M1021.06,-31.42C1038,-35.83 1057.45,-40.89 1076.75,-45.91"/>
<polygon fill="#888888" stroke="#888888" points="1075.94,-49.32 1086.5,-48.45 1077.7,-42.54 1075.94,-49.32"/>
<path fill="none" stroke="#888888" stroke-dasharray="5,2" d="M1152.08,-31.49C1167.19,-35.77 1184.45,-40.66 1201.63,-45.53"/>
<polygon fill="#888888" stroke="#888888" points="1201.15,-49.03 1211.73,-48.39 1203.06,-42.3 1201.15,-49.03"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

Before After
Before After