arborist/docs/diagrams/three-layer-stack.svg
russell@unturf.com 06e6c7a918
docs: 3 concepts diagrams + Python-library cookbook recipes
Address Grok's two minor-improvement flags on the docs.

New docs/diagrams/{three-layer-stack,cache-key-8dim,falsification-states}.{dot,svg,png}
embedded into docs/_source/concepts.rst — visual scaffolding for the
3-layer stack, 8-dim cache_key composition, and falsification state
machine (previously prose+tables only).

docs/_source/cookbook.rst gains a "Use arborist as a Python library"
section: open_store + ingest_documents, custom Source subclass,
audit-chain walk + verify, Merkle proof round-trip, programmatic
arborist.qa.query() with OpenAICompatibleClient + StubClient swap.

Every Python recipe smoke-tested against a scratch DB before publish.
make docs-api: 0 new warnings. make test: 2557 passed.
2026-05-24 14:12:26 -04:00

226 lines
18 KiB
XML

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.43.0 (0)
-->
<!-- Title: three_layer_stack Pages: 1 -->
<svg width="1377pt" height="415pt"
viewBox="0.00 0.00 1376.54 414.50" 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 410.5)">
<title>three_layer_stack</title>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-410.5 1372.54,-410.5 1372.54,4 -4,4"/>
<g id="clust1" class="cluster">
<title>cluster_surface</title>
<path fill="#fff7e6" stroke="black" d="M34.54,-220C34.54,-220 586.54,-220 586.54,-220 592.54,-220 598.54,-226 598.54,-232 598.54,-232 598.54,-291 598.54,-291 598.54,-297 592.54,-303 586.54,-303 586.54,-303 34.54,-303 34.54,-303 28.54,-303 22.54,-297 22.54,-291 22.54,-291 22.54,-232 22.54,-232 22.54,-226 28.54,-220 34.54,-220"/>
<text text-anchor="middle" x="157.04" y="-227.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="14.00">SURFACE — ingested documents</text>
</g>
<g id="clust2" class="cluster">
<title>cluster_core</title>
<path fill="#e7ffe7" stroke="black" d="M618.54,-220C618.54,-220 1043.54,-220 1043.54,-220 1049.54,-220 1055.54,-226 1055.54,-232 1055.54,-232 1055.54,-291 1055.54,-291 1055.54,-297 1049.54,-303 1043.54,-303 1043.54,-303 618.54,-303 618.54,-303 612.54,-303 606.54,-297 606.54,-291 606.54,-291 606.54,-232 606.54,-232 606.54,-226 612.54,-220 618.54,-220"/>
<text text-anchor="middle" x="686.54" y="-227.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="14.00">CORE — distillates</text>
</g>
<g id="clust3" class="cluster">
<title>cluster_prov</title>
<path fill="#e6f0ff" stroke="black" d="M1075.54,-8C1075.54,-8 1348.54,-8 1348.54,-8 1354.54,-8 1360.54,-14 1360.54,-20 1360.54,-20 1360.54,-293.5 1360.54,-293.5 1360.54,-299.5 1354.54,-305.5 1348.54,-305.5 1348.54,-305.5 1075.54,-305.5 1075.54,-305.5 1069.54,-305.5 1063.54,-299.5 1063.54,-293.5 1063.54,-293.5 1063.54,-20 1063.54,-20 1063.54,-14 1069.54,-8 1075.54,-8"/>
<text text-anchor="middle" x="1212.04" y="-15.8" font-family="Helvetica,sans-Serif" font-weight="bold" font-size="14.00">PROVIDENCE CACHE — Q&amp;A records</text>
</g>
<!-- s_wiki -->
<g id="node1" class="node">
<title>s_wiki</title>
<path fill="#fff0d0" stroke="black" d="M272.54,-291C272.54,-291 200.54,-291 200.54,-291 194.54,-291 188.54,-285 188.54,-279 188.54,-279 188.54,-267 188.54,-267 188.54,-261 194.54,-255 200.54,-255 200.54,-255 272.54,-255 272.54,-255 278.54,-255 284.54,-261 284.54,-267 284.54,-267 284.54,-279 284.54,-279 284.54,-285 278.54,-291 272.54,-291"/>
<text text-anchor="middle" x="236.54" y="-276" font-family="Helvetica,sans-Serif" font-size="10.00">Wikipedia</text>
<text text-anchor="middle" x="236.54" y="-265" font-family="Helvetica,sans-Serif" font-size="10.00">XML / SQL dump</text>
</g>
<!-- s_root -->
<g id="node6" class="node">
<title>s_root</title>
<polygon fill="#ffe2a8" stroke="black" points="425.04,-390 246.04,-390 246.04,-349 431.04,-349 431.04,-384 425.04,-390"/>
<polyline fill="none" stroke="black" points="425.04,-390 425.04,-384 "/>
<polyline fill="none" stroke="black" points="431.04,-384 425.04,-384 "/>
<text text-anchor="middle" x="338.54" y="-378" font-family="Helvetica,sans-Serif" font-size="10.00">document_root (32 bytes)</text>
<text text-anchor="middle" x="338.54" y="-367" font-family="Helvetica,sans-Serif" font-size="10.00">Merkle root over canonical chunks</text>
<text text-anchor="middle" x="338.54" y="-356" font-family="Helvetica,sans-Serif" font-size="10.00">identity = content, not URI</text>
</g>
<!-- s_wiki&#45;&gt;s_root -->
<g id="edge1" class="edge">
<title>s_wiki&#45;&gt;s_root</title>
<path fill="none" stroke="black" d="M255.25,-291.33C270.51,-305.47 292.32,-325.68 309.8,-341.87"/>
<polygon fill="black" stroke="black" points="307.77,-344.77 317.48,-348.99 312.53,-339.63 307.77,-344.77"/>
</g>
<!-- s_html -->
<g id="node2" class="node">
<title>s_html</title>
<path fill="#fff0d0" stroke="black" d="M362.04,-291C362.04,-291 315.04,-291 315.04,-291 309.04,-291 303.04,-285 303.04,-279 303.04,-279 303.04,-267 303.04,-267 303.04,-261 309.04,-255 315.04,-255 315.04,-255 362.04,-255 362.04,-255 368.04,-255 374.04,-261 374.04,-267 374.04,-267 374.04,-279 374.04,-279 374.04,-285 368.04,-291 362.04,-291"/>
<text text-anchor="middle" x="338.54" y="-276" font-family="Helvetica,sans-Serif" font-size="10.00">HTML page</text>
<text text-anchor="middle" x="338.54" y="-265" font-family="Helvetica,sans-Serif" font-size="10.00">(crawler)</text>
</g>
<!-- s_html&#45;&gt;s_root -->
<g id="edge2" class="edge">
<title>s_html&#45;&gt;s_root</title>
<path fill="none" stroke="black" d="M338.54,-291.33C338.54,-304.58 338.54,-323.16 338.54,-338.77"/>
<polygon fill="black" stroke="black" points="335.04,-338.99 338.54,-348.99 342.04,-338.99 335.04,-338.99"/>
</g>
<!-- s_grok -->
<g id="node3" class="node">
<title>s_grok</title>
<path fill="#fff0d0" stroke="black" d="M460.54,-291C460.54,-291 404.54,-291 404.54,-291 398.54,-291 392.54,-285 392.54,-279 392.54,-279 392.54,-267 392.54,-267 392.54,-261 398.54,-255 404.54,-255 404.54,-255 460.54,-255 460.54,-255 466.54,-255 472.54,-261 472.54,-267 472.54,-267 472.54,-279 472.54,-279 472.54,-285 466.54,-291 460.54,-291"/>
<text text-anchor="middle" x="432.54" y="-276" font-family="Helvetica,sans-Serif" font-size="10.00">Grok export</text>
<text text-anchor="middle" x="432.54" y="-265" font-family="Helvetica,sans-Serif" font-size="10.00">conversation</text>
</g>
<!-- s_grok&#45;&gt;s_root -->
<g id="edge3" class="edge">
<title>s_grok&#45;&gt;s_root</title>
<path fill="none" stroke="black" d="M415.3,-291.33C401.23,-305.47 381.13,-325.68 365.02,-341.87"/>
<polygon fill="black" stroke="black" points="362.51,-339.44 357.94,-348.99 367.47,-344.37 362.51,-339.44"/>
</g>
<!-- s_vcs -->
<g id="node4" class="node">
<title>s_vcs</title>
<path fill="#fff0d0" stroke="black" d="M574.04,-291C574.04,-291 503.04,-291 503.04,-291 497.04,-291 491.04,-285 491.04,-279 491.04,-279 491.04,-267 491.04,-267 491.04,-261 497.04,-255 503.04,-255 503.04,-255 574.04,-255 574.04,-255 580.04,-255 586.04,-261 586.04,-267 586.04,-267 586.04,-279 586.04,-279 586.04,-285 580.04,-291 574.04,-291"/>
<text text-anchor="middle" x="538.54" y="-276" font-family="Helvetica,sans-Serif" font-size="10.00">git / hg</text>
<text text-anchor="middle" x="538.54" y="-265" font-family="Helvetica,sans-Serif" font-size="10.00">repo HEAD walk</text>
</g>
<!-- s_vcs&#45;&gt;s_root -->
<g id="edge4" class="edge">
<title>s_vcs&#45;&gt;s_root</title>
<path fill="none" stroke="black" d="M508.93,-291.02C500.19,-295.87 490.56,-301.03 481.54,-305.5 453.5,-319.39 421.7,-333.55 395.22,-344.92"/>
<polygon fill="black" stroke="black" points="393.54,-341.83 385.72,-348.98 396.29,-348.27 393.54,-341.83"/>
</g>
<!-- s_tex -->
<g id="node5" class="node">
<title>s_tex</title>
<path fill="#fff0d0" stroke="black" d="M158.54,-291C158.54,-291 46.54,-291 46.54,-291 40.54,-291 34.54,-285 34.54,-279 34.54,-279 34.54,-267 34.54,-267 34.54,-261 40.54,-255 46.54,-255 46.54,-255 158.54,-255 158.54,-255 164.54,-255 170.54,-261 170.54,-267 170.54,-267 170.54,-279 170.54,-279 170.54,-285 164.54,-291 158.54,-291"/>
<text text-anchor="middle" x="102.54" y="-276" font-family="Helvetica,sans-Serif" font-size="10.00">textbook_tex</text>
<text text-anchor="middle" x="102.54" y="-265" font-family="Helvetica,sans-Serif" font-size="10.00">Project Gutenberg LaTeX</text>
</g>
<!-- s_tex&#45;&gt;s_root -->
<g id="edge5" class="edge">
<title>s_tex&#45;&gt;s_root</title>
<path fill="none" stroke="black" d="M144.06,-291.09C155.55,-295.78 168.02,-300.86 179.54,-305.5 212.47,-318.76 249.32,-333.38 279.25,-345.19"/>
<polygon fill="black" stroke="black" points="278.12,-348.51 288.71,-348.92 280.69,-341.99 278.12,-348.51"/>
</g>
<!-- c_proof -->
<g id="node10" class="node">
<title>c_proof</title>
<polygon fill="#b8e6b8" stroke="black" points="861.54,-406.5 705.54,-406.5 705.54,-332.5 867.54,-332.5 867.54,-400.5 861.54,-406.5"/>
<polyline fill="none" stroke="black" points="861.54,-406.5 861.54,-400.5 "/>
<polyline fill="none" stroke="black" points="867.54,-400.5 861.54,-400.5 "/>
<text text-anchor="middle" x="786.54" y="-394.5" font-family="Helvetica,sans-Serif" font-size="10.00">derivations.proof_blob</text>
<text text-anchor="middle" x="786.54" y="-383.5" font-family="Helvetica,sans-Serif" font-size="10.00">per contributing chunk:</text>
<text text-anchor="middle" x="786.54" y="-372.5" font-family="Helvetica,sans-Serif" font-size="10.00"> &#160;• leaf_hash</text>
<text text-anchor="middle" x="786.54" y="-361.5" font-family="Helvetica,sans-Serif" font-size="10.00"> &#160;• sibling path</text>
<text text-anchor="middle" x="786.54" y="-350.5" font-family="Helvetica,sans-Serif" font-size="10.00"> &#160;• verify_proof() ≡ True</text>
<text text-anchor="middle" x="786.54" y="-339.5" font-family="Helvetica,sans-Serif" font-size="10.00">binding survives compression</text>
</g>
<!-- s_root&#45;&gt;c_proof -->
<g id="edge6" class="edge">
<title>s_root&#45;&gt;c_proof</title>
<path fill="none" stroke="#2d6a2d" d="M431.09,-369.5C508.46,-369.5 618.19,-369.5 695.21,-369.5"/>
<polygon fill="#2d6a2d" stroke="#2d6a2d" points="695.38,-373 705.38,-369.5 695.38,-366 695.38,-373"/>
<text text-anchor="middle" x="568.29" y="-359.3" font-family="Helvetica,sans-Serif" font-size="9.00">ingested chunks</text>
<text text-anchor="middle" x="568.29" y="-349.3" font-family="Helvetica,sans-Serif" font-size="9.00">bind cores back</text>
</g>
<!-- p_key -->
<g id="node11" class="node">
<title>p_key</title>
<polygon fill="#cfdfff" stroke="black" points="1245.04,-106 1078.04,-106 1078.04,-43 1251.04,-43 1251.04,-100 1245.04,-106"/>
<polyline fill="none" stroke="black" points="1245.04,-106 1245.04,-100 "/>
<polyline fill="none" stroke="black" points="1251.04,-100 1245.04,-100 "/>
<text text-anchor="middle" x="1164.54" y="-94" font-family="Helvetica,sans-Serif" font-size="10.00">cache_key (8&#45;dim)</text>
<text text-anchor="middle" x="1164.54" y="-83" font-family="Helvetica,sans-Serif" font-size="10.00">source_root | question_hash |</text>
<text text-anchor="middle" x="1164.54" y="-72" font-family="Helvetica,sans-Serif" font-size="10.00">model_profile | conversation |</text>
<text text-anchor="middle" x="1164.54" y="-61" font-family="Helvetica,sans-Serif" font-size="10.00">governance_policy | schema_v |</text>
<text text-anchor="middle" x="1164.54" y="-50" font-family="Helvetica,sans-Serif" font-size="10.00">canonicalization_v | chunking_v</text>
</g>
<!-- s_root&#45;&gt;p_key -->
<g id="edge11" class="edge">
<title>s_root&#45;&gt;p_key</title>
<path fill="none" stroke="#1a3a8a" d="M245.85,-363.03C161.82,-355.9 46.9,-340 18.54,-305.5 -5.6,-276.15 -6.6,-248.5 18.54,-220 87.62,-141.66 803.29,-95.13 1067.36,-80.51"/>
<polygon fill="#1a3a8a" stroke="#1a3a8a" points="1067.9,-83.99 1077.7,-79.95 1067.52,-77 1067.9,-83.99"/>
<text text-anchor="middle" x="71.04" y="-204.8" font-family="Helvetica,sans-Serif" font-size="9.00">source_root</text>
</g>
<!-- c_first -->
<g id="node7" class="node">
<title>c_first</title>
<path fill="#d0f0d0" stroke="black" d="M1031.54,-291C1031.54,-291 957.54,-291 957.54,-291 951.54,-291 945.54,-285 945.54,-279 945.54,-279 945.54,-267 945.54,-267 945.54,-261 951.54,-255 957.54,-255 957.54,-255 1031.54,-255 1031.54,-255 1037.54,-255 1043.54,-261 1043.54,-267 1043.54,-267 1043.54,-279 1043.54,-279 1043.54,-285 1037.54,-291 1031.54,-291"/>
<text text-anchor="middle" x="994.54" y="-276" font-family="Helvetica,sans-Serif" font-size="10.00">first_sentence</text>
<text text-anchor="middle" x="994.54" y="-265" font-family="Helvetica,sans-Serif" font-size="10.00">(lead extraction)</text>
</g>
<!-- c_first&#45;&gt;c_proof -->
<g id="edge7" class="edge">
<title>c_first&#45;&gt;c_proof</title>
<path fill="none" stroke="black" d="M964.43,-291.09C955.54,-295.93 945.73,-301.09 936.54,-305.5 917.52,-314.63 896.84,-323.79 877.08,-332.19"/>
<polygon fill="black" stroke="black" points="875.62,-329.01 867.77,-336.12 878.34,-335.46 875.62,-329.01"/>
</g>
<!-- c_tfidf -->
<g id="node8" class="node">
<title>c_tfidf</title>
<path fill="#d0f0d0" stroke="black" d="M695.04,-291C695.04,-291 630.04,-291 630.04,-291 624.04,-291 618.04,-285 618.04,-279 618.04,-279 618.04,-267 618.04,-267 618.04,-261 624.04,-255 630.04,-255 630.04,-255 695.04,-255 695.04,-255 701.04,-255 707.04,-261 707.04,-267 707.04,-267 707.04,-279 707.04,-279 707.04,-285 701.04,-291 695.04,-291"/>
<text text-anchor="middle" x="662.54" y="-276" font-family="Helvetica,sans-Serif" font-size="10.00">tfidf</text>
<text text-anchor="middle" x="662.54" y="-265" font-family="Helvetica,sans-Serif" font-size="10.00">(keyword core)</text>
</g>
<!-- c_tfidf&#45;&gt;c_proof -->
<g id="edge8" class="edge">
<title>c_tfidf&#45;&gt;c_proof</title>
<path fill="none" stroke="black" d="M684.99,-291.11C697.83,-300.9 714.55,-313.64 730.71,-325.95"/>
<polygon fill="black" stroke="black" points="729,-329.05 739.08,-332.33 733.25,-323.49 729,-329.05"/>
</g>
<!-- c_recur -->
<g id="node9" class="node">
<title>c_recur</title>
<path fill="#d0f0d0" stroke="black" d="M836.04,-291C836.04,-291 737.04,-291 737.04,-291 731.04,-291 725.04,-285 725.04,-279 725.04,-279 725.04,-267 725.04,-267 725.04,-261 731.04,-255 737.04,-255 737.04,-255 836.04,-255 836.04,-255 842.04,-255 848.04,-261 848.04,-267 848.04,-267 848.04,-279 848.04,-279 848.04,-285 842.04,-291 836.04,-291"/>
<text text-anchor="middle" x="786.54" y="-276" font-family="Helvetica,sans-Serif" font-size="10.00">depth&#45;N → depth&#45;N+1</text>
<text text-anchor="middle" x="786.54" y="-265" font-family="Helvetica,sans-Serif" font-size="10.00">recursive distillation</text>
</g>
<!-- c_recur&#45;&gt;c_recur -->
<g id="edge10" class="edge">
<title>c_recur&#45;&gt;c_recur</title>
<path fill="none" stroke="#2d6a2d" d="M848.2,-266C858.74,-266.96 866.04,-269.29 866.04,-273 866.04,-275.38 863.04,-277.19 858.12,-278.44"/>
<polygon fill="#2d6a2d" stroke="#2d6a2d" points="857.54,-274.99 848.2,-280 858.63,-281.9 857.54,-274.99"/>
<text text-anchor="middle" x="896.54" y="-270.8" font-family="Helvetica,sans-Serif" font-size="9.00">self&#45;recursion</text>
</g>
<!-- c_recur&#45;&gt;c_proof -->
<g id="edge9" class="edge">
<title>c_recur&#45;&gt;c_proof</title>
<path fill="none" stroke="black" d="M786.54,-291.33C786.54,-300.14 786.54,-311.31 786.54,-322.4"/>
<polygon fill="black" stroke="black" points="783.04,-322.49 786.54,-332.49 790.04,-322.49 783.04,-322.49"/>
</g>
<!-- p_proof -->
<g id="node14" class="node">
<title>p_proof</title>
<polygon fill="#a8c8ff" stroke="black" points="1176.04,-390 1017.04,-390 1017.04,-349 1182.04,-349 1182.04,-384 1176.04,-390"/>
<polyline fill="none" stroke="black" points="1176.04,-390 1176.04,-384 "/>
<polyline fill="none" stroke="black" points="1182.04,-384 1176.04,-384 "/>
<text text-anchor="middle" x="1099.54" y="-378" font-family="Helvetica,sans-Serif" font-size="10.00">merkle_proof</text>
<text text-anchor="middle" x="1099.54" y="-367" font-family="Helvetica,sans-Serif" font-size="10.00">binds answer ↔ source chunks</text>
<text text-anchor="middle" x="1099.54" y="-356" font-family="Helvetica,sans-Serif" font-size="10.00">verifies offline, across peers</text>
</g>
<!-- c_proof&#45;&gt;p_proof -->
<g id="edge12" class="edge">
<title>c_proof&#45;&gt;p_proof</title>
<path fill="none" stroke="#1a3a8a" stroke-dasharray="5,2" d="M867.55,-369.5C910.15,-369.5 962.56,-369.5 1006.78,-369.5"/>
<polygon fill="#1a3a8a" stroke="#1a3a8a" points="1006.96,-373 1016.96,-369.5 1006.96,-366 1006.96,-373"/>
<text text-anchor="middle" x="942.29" y="-359.3" font-family="Helvetica,sans-Serif" font-size="9.00">proof structure</text>
<text text-anchor="middle" x="942.29" y="-349.3" font-family="Helvetica,sans-Serif" font-size="9.00">reused</text>
</g>
<!-- p_audit -->
<g id="node12" class="node">
<title>p_audit</title>
<path fill="#cfdfff" stroke="black" d="M1248.04,-184C1248.04,-184 1087.04,-184 1087.04,-184 1081.04,-184 1075.04,-178 1075.04,-172 1075.04,-172 1075.04,-155 1075.04,-155 1075.04,-149 1081.04,-143 1087.04,-143 1087.04,-143 1248.04,-143 1248.04,-143 1254.04,-143 1260.04,-149 1260.04,-155 1260.04,-155 1260.04,-172 1260.04,-172 1260.04,-178 1254.04,-184 1248.04,-184"/>
<text text-anchor="middle" x="1167.54" y="-172" font-family="Helvetica,sans-Serif" font-size="10.00">audit_mode ∈</text>
<text text-anchor="middle" x="1167.54" y="-161" font-family="Helvetica,sans-Serif" font-size="10.00">{STRICT, HYBRID, UNGROUNDED}</text>
<text text-anchor="middle" x="1167.54" y="-150" font-family="Helvetica,sans-Serif" font-size="10.00">set by verifier, never asserted</text>
</g>
<!-- p_key&#45;&gt;p_audit -->
<!-- p_state -->
<g id="node13" class="node">
<title>p_state</title>
<path fill="#cfdfff" stroke="black" d="M1241.54,-293.5C1241.54,-293.5 1089.54,-293.5 1089.54,-293.5 1083.54,-293.5 1077.54,-287.5 1077.54,-281.5 1077.54,-281.5 1077.54,-264.5 1077.54,-264.5 1077.54,-258.5 1083.54,-252.5 1089.54,-252.5 1089.54,-252.5 1241.54,-252.5 1241.54,-252.5 1247.54,-252.5 1253.54,-258.5 1253.54,-264.5 1253.54,-264.5 1253.54,-281.5 1253.54,-281.5 1253.54,-287.5 1247.54,-293.5 1241.54,-293.5"/>
<text text-anchor="middle" x="1165.54" y="-281.5" font-family="Helvetica,sans-Serif" font-size="10.00">falsification_state ∈</text>
<text text-anchor="middle" x="1165.54" y="-270.5" font-family="Helvetica,sans-Serif" font-size="10.00">{live, failed, stale, quarantined}</text>
<text text-anchor="middle" x="1165.54" y="-259.5" font-family="Helvetica,sans-Serif" font-size="10.00">cache reads filter on state=&#39;live&#39;</text>
</g>
<!-- p_audit&#45;&gt;p_state -->
<!-- p_state&#45;&gt;p_proof -->
</g>
</svg>