Document Jerboa site sync workflow

ober

689621839604f01dcdd629a601c9f8dc800e29d7

diff --git a/Makefile b/Makefile
index e1f2e20..871e576 100644
--- a/Makefile
+++ b/Makefile
@@ -17,7 +17,7 @@ STATIC_XPATCH ?= $(JERBOA_HOME)/build/chez/xc-$(STATIC_TARGET_MACHINE)/s/xpatch
 STATIC_MUSL_CC ?= x86_64-linux-musl-gcc
 STATIC_OS_LIBS ?= -lm -ldl -lpthread -static -no-pie -lstdc++
 
-.PHONY: deps binary static-binary clean distclean
+.PHONY: deps binary static-binary sync-jerboa clean distclean
 
 deps:
 	$(MAKE) -C $(JERBOA_SINATRA) build
@@ -33,6 +33,9 @@ static-binary: deps
 	mkdir -p $(dir $(STATIC_BINARY_OUTPUT))
 	JERBOA_HOME="$(JERBOA_HOME)" $(JERBUILD) binary --static-native --cc "$(STATIC_MUSL_CC)" --rust-target "$(STATIC_RUST_TARGET)" --csv-dir "$(STATIC_CSV_DIR)" --xpatch "$(STATIC_XPATCH)" --os-libs "$(STATIC_OS_LIBS)" --libdirs "$(SINATRA_LIBDIR)" $(ENTRY) $(STATIC_BINARY_OUTPUT)
 
+sync-jerboa:
+	tools/sync-jerboa --jerboa "$(JERBOA_HOME)" --write
+
 clean:
 	rm -rf build
 
diff --git a/README.md b/README.md
index d58774f..879a88b 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,21 @@
 
 Private jerboa.sh website content built with `jerboa-sinatra`.
 
+## Source of Truth
+
+This repo owns the private site presentation and deployable web binary.
+Canonical docs, examples, public assets, and machine-readable API or
+feature data live in `~/mine/jerboa`.
+
+Sync the pinned Jerboa metadata snapshot with:
+
+```sh
+make sync-jerboa
+```
+
+See [`docs/site-sync.md`](docs/site-sync.md) for the update workflow and
+LLM-assisted documentation guardrails.
+
 ## Build
 
 This repo keeps only the site entry point and content. It builds against the local framework checkout at `~/mine/jerboa-sinatra` by default.
diff --git a/data/jerboa-source.json b/data/jerboa-source.json
new file mode 100644
index 0000000..bc12acb
--- /dev/null
+++ b/data/jerboa-source.json
@@ -0,0 +1,149 @@
+{
+  "schema": "jerboa.site-source.v1",
+  "repo": {
+    "path": "/Users/user/mine/jerboa",
+    "remote": "git@git.sr.ht:~lisp/jerboa",
+    "branch": "master",
+    "commit": "1cf9afb74a3d5da7ff9ee76e477dae2332364f74",
+    "version": "0.2.3",
+    "source_tree": "HEAD",
+    "working_tree_dirty": true
+  },
+  "generated_at": "2026-06-19T21:47:35Z",
+  "site_contract": {
+    "human_docs": "docs/",
+    "machine_data": "data/",
+    "examples": "examples/",
+    "assets": "assets/"
+  },
+  "canonical_sources": {
+    "docs": [
+      {"path": "docs/Chez-changes.md", "sha256": "f8866866fe2eac24b969470b41c5e89ed63703b831b6c43ae37053dcae97facf", "bytes": 4500},
+      {"path": "docs/Future-proofing.md", "sha256": "927b8f6ec50d338e570c90e0f1decf7cfd8847824646ceae6b3079910841a310", "bytes": 17232},
+      {"path": "docs/JERBOA-LANG.md", "sha256": "60acc5ef32957b9e4608a65c237993e5699bcead31e570ad377175931573f49f", "bytes": 27440},
+      {"path": "docs/Philosophy.md", "sha256": "8d388cb1162837f0a53311f71bcc4e98dbbd184832ca83c649f58b2170c70669", "bytes": 13970},
+      {"path": "docs/actor-model.md", "sha256": "6a9e075ae78290dcf56b8038b47f530d7541f980e7cf9eccc52065bf4e4ea8f8", "bytes": 144631},
+      {"path": "docs/ai-threat.md", "sha256": "528fe24f97745fa6589cd9b4e4aad6d649f9f24f4c2619103df2cb6a01f4799d", "bytes": 9587},
+      {"path": "docs/anti-cookbook.md", "sha256": "c6fdbd78121ff4ee0b9f142b46ecfc0f5b07d62a7c7ced328620839fcd996701", "bytes": 9876},
+      {"path": "docs/api-index.md", "sha256": "21516670e6c9433c5495c8191634999f53837a513fb9f10cb451cc30c8a87c18", "bytes": 710877},
+      {"path": "docs/aproc.md", "sha256": "b872800394492c25b4fc5565b3b501daba95c5b69c3187dba30f39a7cfde8996", "bytes": 14247},
+      {"path": "docs/architecture-split.md", "sha256": "f578a99486ed230e16a5efd24c39171078bf21c35af0eb5606c85f1e9834ebd2", "bytes": 9168},
+      {"path": "docs/async.md", "sha256": "9ae56e12f9cb819e97f2a9417a57a15baf01b4e7a82e24522f51d381ad47252a", "bytes": 16426},
+      {"path": "docs/build.md", "sha256": "482c11bba136154eef7565ab830e3664032f5a4452410a01d6d37e1ea2308490", "bytes": 18569},
+      {"path": "docs/bundling-chez.md", "sha256": "39aeb7852d5ed9654d43f22ebfc88f40f13979f37566fe81eb18e5d44b49690e", "bytes": 8203},
+      {"path": "docs/capability.md", "sha256": "1cc15b742a7a557f1833d2fd60f9ea96e2f6b7d999bb9e5887332b2e0829a368", "bytes": 15647},
+      {"path": "docs/chez-fork.md", "sha256": "71b3dadb5df1a487f7eea7980aa3d8b4dd47931381a4ecea98da31b8d05807f2", "bytes": 5413},
+      {"path": "docs/chez-gc-findings.md", "sha256": "c2561cfeca85fc99752d3b277ad764056ab4a13401270619ea8a2fd5b8fcf8b5", "bytes": 17378},
+      {"path": "docs/chez-hardening.md", "sha256": "ab1660f1d0491fb5b32b7441f6e3b7d60a2decb2a5922882822d53df5098d7f7", "bytes": 21668},
+      {"path": "docs/chez-limits.md", "sha256": "f34f7d0304ecbc97ed0970410a60de3ddb5d82f9a231c050d1d660530ef639e5", "bytes": 12635},
+      {"path": "docs/clojure-vs-jerboa.md", "sha256": "8899f0f39cf7abb3bce70221f4b1955b908e6301475b527022f5d87b15b81c14", "bytes": 106638},
+      {"path": "docs/compiling-gerbil-projects.md", "sha256": "d26ba7b07ae7cf2362cb2c81025140959fc0a02ac02b070ebd4bfc6f2395db07", "bytes": 12214},
+      {"path": "docs/completion.md", "sha256": "8570b09216316c8431ed470613758fcc18e2e9a753fd9c23a02ffffd6ae8b6d9", "bytes": 15964},
+      {"path": "docs/concurrency-extended.md", "sha256": "2e1b25d164bb1527a2d91db2a92bd1015fa8a61e8f5926d3e61d6e0dbad06932", "bytes": 20410},
+      {"path": "docs/concurrency.md", "sha256": "46bb5891a0fb9563f69409333b563eb69e87c8f11f886323ee93bb7505b1e0e6", "bytes": 7091},
+      {"path": "docs/contracts.md", "sha256": "3a347ad4b546d1b3516854efa59fbb14a306496dcc81b4b657d497686f4db3ce", "bytes": 13865},
+      {"path": "docs/core-async.md", "sha256": "b82e8413a648578bf62ddc489e6afb44341cd96daadf564bf165930d2d39a934", "bytes": 34686},
+      {"path": "docs/cp0-passes.md", "sha256": "903a2696d7b1cef0fe8cbbd96bb4a2b393813d0dbb59a2524113dde918b5f34c", "bytes": 5780},
+      {"path": "docs/cross-compile.md", "sha256": "b8251cbe347ee0deb8d3fe80ec83513653de707bc0de4c0e6e68be9d19f31034", "bytes": 10227},
+      {"path": "docs/data-structures.md", "sha256": "681c9e77f2f22b4803108892198a90f6ed171e38fb9757abd05a3865865cf23f", "bytes": 24145},
+      {"path": "docs/devex.md", "sha256": "0145c797ad9e4a23160b81d35d9f6af55d9c518e0960e661691ea75abe78f128", "bytes": 7168},
+      {"path": "docs/disassembly.md", "sha256": "1732617f8f965b90e1ed25639494f00be244added6765d277b4f4df8249ac967", "bytes": 11495},
+      {"path": "docs/distributed.md", "sha256": "24e0142e4ce6f9b4498c444c92f8739bccd0b661a2826f5578d3774d3deb1a01", "bytes": 29049},
+      {"path": "docs/divergence.md", "sha256": "995a4ce967b81468f1e81a222000949147167360fee59b308a74ed4c44f2eba3", "bytes": 41668},
+      {"path": "docs/effects.md", "sha256": "bddd0732484bc9c7c7ea820bea3f9b1117c2f18e334339992255e95e64b30e13", "bytes": 23399},
+      {"path": "docs/ffi.md", "sha256": "ed1408793c740aa3746f90b82087251492d7fc58c46fe26cd42e032bddb9904a", "bytes": 7455},
+      {"path": "docs/fiber.md", "sha256": "9f62082caf1ccaf780aea611cb6bc7d90fb3ff22b7cbc10cd348cb7ec426d8e2", "bytes": 16242},
+      {"path": "docs/finalizer-safety-net.md", "sha256": "0036c32bf19c2bc023318eb519556aabbf44f37dee1f625bce32093af06892eb", "bytes": 3007},
+      {"path": "docs/gerbil-contracts.md", "sha256": "960764c09b0a7cf66b87224d3e4f46484268b4b8b933be8f70810da941ea2ce1", "bytes": 25456},
+      {"path": "docs/gerbil-like.md", "sha256": "231cfb62b628d511bd768859e4600a0e39e2a0796bec01b9439080f98da89699", "bytes": 16316},
+      {"path": "docs/green-wins.md", "sha256": "eb7d9e2cca2fdd89f3c65ada5789ede59a42bb4328a8971742444e8a23e2495a", "bytes": 18945},
+      {"path": "docs/harden-usage.md", "sha256": "9d417190bb4533e6708b00eb186ca0694a61f1ac5a13ccd3257d984c91cb8466", "bytes": 15819},
+      {"path": "docs/harden.md", "sha256": "a9451f3f8f3004a7a91eeb56ac838c4051cd830a2df420a41bd5518395e48ce9", "bytes": 23959},
+      {"path": "docs/hide.md", "sha256": "9021dbbbfe5081dda3cda89478acc39add425de4cd9eaa0546a1be065378580e", "bytes": 24491},
+      {"path": "docs/import-conflicts.md", "sha256": "608c581b1c4246fb6eb2ba05c60725347336e12340541ec9a7684d7e0c237eee", "bytes": 4955},
+      {"path": "docs/index.md", "sha256": "c859b43f46dfbb9af761f8b7dfba21f11c5c2a73ec58fc8ae6b1a441368c62ae", "bytes": 9768},
+      {"path": "docs/jerboa-code-typed-static-plan.md", "sha256": "e279b3a0d59a57c6bf72971910077a6b800d39331063b3c472f73590076ca065", "bytes": 9902},
+      {"path": "docs/jerboa-db.md", "sha256": "8262d1272ab7e0384050e83d5508f15ab785059cf7f47504a64df767bcf09fd6", "bytes": 66395},
+      {"path": "docs/jerboa-edge.md", "sha256": "2798ba6b8aabd2c73865a5dd9adb6f998c91303f214692cf6d21f9e33ae5c3d5", "bytes": 37373},
+      {"path": "docs/jerboa-for-clojure-devs.md", "sha256": "abafe620f03e4c89975bdaab0dde22c26a1df8b4c9392ac13b7496960d0cde9d", "bytes": 16855},
+      {"path": "docs/jerboa-on-jerboa-idiom.md", "sha256": "ec1e5d65efba9a8b128e0f308463c4eac396eef28f58449d791962a55bc9dd8f", "bytes": 9116},
+      {"path": "docs/jerboa-on-jerboa.md", "sha256": "65550f8c737540aeda8187423b68d94ee167db317da6f229c67e54b2a62792ae", "bytes": 12065},
+      {"path": "docs/jerboa-to-llvmir.md", "sha256": "d53c6f852a9e8de23f95cdd8c2431850e32585fef00e9cf79a1ee2a793331392", "bytes": 16756},
+      {"path": "docs/jerboa-to-rust.md", "sha256": "d4b2ab445f9e98f449cfdffcfd4b3ab28082f6fa5aca16ea5956c71c3ddc703b", "bytes": 31844},
+      {"path": "docs/jpkg-guide.md", "sha256": "2e2cbb29b2a078ddcb9bdf19f263e6011dc4a7f7594f7b36083f0093db86e591", "bytes": 19114},
+      {"path": "docs/jpkg-migration.md", "sha256": "3a06019d9a1da9aeb750a3c2723274b4e966953b9f91578b149a537d8a80cb97", "bytes": 9434},
+      {"path": "docs/jpkg-plan.md", "sha256": "9cbda6b05fc3512e1ccc957552d2a1d807e5d232177a0abbed0d387a8f40e54e", "bytes": 27492},
+      {"path": "docs/libraries.md", "sha256": "5f7f38604019921fbe7bacab0ac9bfc7b9b5dbe922a54a70e9fb7a30a95932fa", "bytes": 9766},
+      {"path": "docs/lightweight-concurrency.md", "sha256": "918e53054edd2fe8e3437ecfddb8f3618b00db87e97a74460786eeb85c905af9", "bytes": 11851},
+      {"path": "docs/limits.md", "sha256": "f87754af6e106eded8f5bc88199c4f47ee0606c920f15db1a72a63e327c87afd", "bytes": 35713},
+      {"path": "docs/llvmir-backend.md", "sha256": "1abcc2f6b00604ad7c98f9d6fa4c10dc71b4a7b79567b865fe7463d69ec14791", "bytes": 10615},
+      {"path": "docs/llvmir-crypto-shim.md", "sha256": "ada3e2399d6e950afec04d437a414cb12db4464d8a0ddcfebde2f5b2abeab486", "bytes": 6591},
+      {"path": "docs/llvmir-handoff.md", "sha256": "1ddc09506439a26b31f74ca81040d7e703b5a5051d847f0310966a56c0e76541", "bytes": 16827},
+      {"path": "docs/mcp-advisor-plan.md", "sha256": "0bcac272d227151a8b220d43e9ce0144090768ff63dd83a53fe2a8d1422a1a11", "bytes": 9449},
+      {"path": "docs/metaprogramming.md", "sha256": "2b429e7a9f7a6980f74440df97917fbff8a2bdbe3be19e79a8a97b01e48df011", "bytes": 22462},
+      {"path": "docs/migration.md", "sha256": "5a71ee619a7e4ae0fd6ac41d1fc7d43a64ca1e50728f44a353540624c6e78bf0", "bytes": 5890},
+      {"path": "docs/module-quickstarts.md", "sha256": "c6c3ea553c6d4434f890602010608832b7b975c279fa943aa9aa1853396a89a3", "bytes": 10665},
+      {"path": "docs/native-rust.md", "sha256": "cda3c58f767c4ca62ea02e7c6bf96693689b4dd8b791026cb6b2dc2e02cdd835", "bytes": 36071},
+      {"path": "docs/optimization.md", "sha256": "bea4c617f7279b29117a34265c29eb57d0d5905da790f827e5a8ae0efd2ec364", "bytes": 11101},
+      {"path": "docs/packages.md", "sha256": "90f5c43c3eced73c3524daa426cdb7f6e0d9b5f523582f31c611909a1d551e3e", "bytes": 5576},
+      {"path": "docs/partial-evaluation.md", "sha256": "669fd4555ad10a283098fbde0b48c8eb5ec27b3fa884a2a741cd6addc8f3350e", "bytes": 7909},
+      {"path": "docs/pattern-matching.md", "sha256": "184eb3cf8194040a37145b288eaa052233810f49b08195ee96c23ce42ca30dfb", "bytes": 21222},
+      {"path": "docs/ports.md", "sha256": "714221e118893c81201688ca23e1bd91a310c51da202e5e7d4e7f1fdc70f31bd", "bytes": 4362},
+      {"path": "docs/protocols.md", "sha256": "4932a2fa39d98e456524c65a0b901e351b48a007580b8a621f803d3f08e40052", "bytes": 16517},
+      {"path": "docs/pure-jerboa-migration.md", "sha256": "6feca7d165f17af1afc434929c34e5442037a819b8490bd0573c45f6208b963d", "bytes": 6713},
+      {"path": "docs/quickstart.md", "sha256": "ed100f8f5897e9aa4ecb81968726d8617ebeb5a7b410fffc24cd6d68722184b3", "bytes": 3930},
+      {"path": "docs/reader-syntax.md", "sha256": "94ecbb3218f3d8f38ddc1c15212c53a550a838222c083e7e3996c85f87671c9d", "bytes": 12692},
+      {"path": "docs/regex.md", "sha256": "b1cb2933a38f92ab76bff3f1def1d7ca1b2f4bd5f5e58e67cd7d4c68e44e1063", "bytes": 41575},
+      {"path": "docs/release-artifacts.md", "sha256": "8760ad5decdf830d72f4d3bfc597401c570d55eea93d1e9f0a70762656e446b0", "bytes": 3876},
+      {"path": "docs/repl-protocol.md", "sha256": "87d0677ebee0b668e98b5a280c4fa67c7688593f22b36e6fb7567eff4c0f613c", "bytes": 5652},
+      {"path": "docs/rocks.md", "sha256": "725f75b0f0322955c43f4acc28d315ac4a4faa52f12769792efdd805f24dc3ff", "bytes": 15949},
+      {"path": "docs/rust-target.md", "sha256": "955445041b8c317e64dce76a6a895f8eb9c4c642aeb80cd209121f0825491434", "bytes": 2728},
+      {"path": "docs/safety-guide.md", "sha256": "01f3933b15f180ce4baabb8d3d836a1212de77fe2ca50f9528adb7b682d6b21d", "bytes": 25529},
+      {"path": "docs/secure.md", "sha256": "dec555db1f1e10bd263171a628c622b86111afe14cb195b5da3bdf2c2f92ef8f", "bytes": 23819},
+      {"path": "docs/security-reference.md", "sha256": "8ba4375cab933df903d4868170bacadd1c039c79800085861a54231e1e585ae4", "bytes": 27738},
+      {"path": "docs/sequences.md", "sha256": "c7f18578a176ed078607ea056aae5cb004d6a26e23d19b9a7ed8ab91b8bc1f14", "bytes": 8686},
+      {"path": "docs/single-binary.md", "sha256": "1e4b044a6ed1234deaff0ff303af90771c56b65653cb0c9d049d377f0c9a02c5", "bytes": 21769},
+      {"path": "docs/single-file-packages.md", "sha256": "ef22b7b6f25d8ce4794b2c8b5e0e1908bfff313187db869a85601194770e94f9", "bytes": 3481},
+      {"path": "docs/site-publishing.md", "sha256": "9c5b5c910afa2d9c0119fe9acb7078b556a7cf6e3fbd3a1368b68ae6400f1321", "bytes": 2633},
+      {"path": "docs/slang.md", "sha256": "c9332327595b1c11d260c8ecf95ed191aaa98b36737057223a9a44d3c4ebc9fd", "bytes": 25195},
+      {"path": "docs/staging.md", "sha256": "3abf62b641b77d1b1a7d9928e8b56e7637198beb3b1eec04a0a6d9c405a7c9d5", "bytes": 18661},
+      {"path": "docs/static-binary-gotchas.md", "sha256": "025bfc9994a963c6a6a8d8ddb69219382e81af7e89f1eb939b835d71b47e54dd", "bytes": 14671},
+      {"path": "docs/status.md", "sha256": "de8dd05f38852924f93628897b2c06d3e352af7ee788c279e2f8d98abdffd51e", "bytes": 24479},
+      {"path": "docs/std-perf.md", "sha256": "7f1b1b24f0d9852764cb8b186744922b5967ab510a2b5ba1dcff76ad6475463b", "bytes": 16289},
+      {"path": "docs/stm.md", "sha256": "2ea2f6b0d250855c5d094915cc84e82f4ecd2f21a38a2e29883d44662d8a9a38", "bytes": 15298},
+      {"path": "docs/testing-and-infrastructure.md", "sha256": "3e637b462ed7fb1d80204f1c9c6d8127d6d551b4ecd230e63c66111cc2131584", "bytes": 30267},
+      {"path": "docs/thread-safe-hash.md", "sha256": "754de955cdd8b42f555beffa487f034c621f89a8001bdf7964907793fb96c843", "bytes": 13855},
+      {"path": "docs/tutorial.md", "sha256": "3c9b4cb01d89b8d1b215f60aa8e4e3c45d1c63e46dc6c10d849731906dcb8814", "bytes": 13261},
+      {"path": "docs/typed-jerboa.md", "sha256": "8072cde2e6254f7fa259a4272cd2c66f72063738a6433398360730111d4aa086", "bytes": 44594},
+      {"path": "docs/typing.md", "sha256": "33bb924a84b11e9cebb94e913d4a366e4fd801840ef3d986cc74002a5dbc4b64", "bytes": 20059},
+      {"path": "docs/uhoh.md", "sha256": "76b155a614925daaee7386fc853995c682fc8c8da729cac032e64958a4634398", "bytes": 11081},
+      {"path": "docs/unification.md", "sha256": "128da45c3d1b9e3bbc20839327086ab68d6dae461c043d2284218e3d38dd8c10", "bytes": 33371},
+      {"path": "docs/vs-rust.md", "sha256": "57c993f42ccd77076c219b9eeae5704915358cf817aeba7cffa8f1c61468bee4", "bytes": 20798},
+      {"path": "docs/wasm-example.md", "sha256": "6addcbe6d93dc565e35b87362dfd25053709f36fcb919237852ada94e8712f59", "bytes": 21859},
+      {"path": "docs/wasm.md", "sha256": "b1acac7f5e84b5ebe4ea059f5c4abad120b9273772e3eb7c01d4c42b40884648", "bytes": 11984},
+      {"path": "docs/whats-new.md", "sha256": "4c8b8eccbe0b465bff1f5b2e64f1b9930c0412013504c33441c44ebcd5db31b1", "bytes": 41856},
+      {"path": "docs/zig-ideas.md", "sha256": "f8b6aacfb187e241f8dc218cab20696a17fc77021d6ce61e7feb638e547992ff", "bytes": 2607}
+    ],
+    "data": [
+      {"path": "data/anti-patterns.sexp", "sha256": "ac23b3998de2243dba862c70763415c9f2f88bcc17a7bc649fb265b5babb881d", "bytes": 41291},
+      {"path": "data/api-signatures.sexp", "sha256": "0861ad11333d334af9284c362027b53ed1f3813cf3e87d595a6b221e4f089f5f", "bytes": 837976},
+      {"path": "data/changelog.sexp", "sha256": "43457a6374436414bdd191067c47f25784ab3789df6d8a7d0be530c3d80029da", "bytes": 16283},
+      {"path": "data/cookbooks.sexp", "sha256": "7f1528b0aab4e37748ff76979fd68ea5d58c0ad5c9eafc2626f631e46e9c3b84", "bytes": 931531},
+      {"path": "data/divergence.sexp", "sha256": "89998895f6f330a8b6d7f8f8b06a8b5aa588d05170b47f12bf88225bd792fae6", "bytes": 58225},
+      {"path": "data/error-fixes.sexp", "sha256": "619bb4158d570ed690b98c1f122bf96a0a4077803662a8711a64183976bd102d", "bytes": 119666},
+      {"path": "data/features.sexp", "sha256": "00bc78b0ab9b99c85eb3735d3ff62f9550b004bb5f993bdefb8f980c89782fdf", "bytes": 203654},
+      {"path": "data/security-rules.sexp", "sha256": "a9c3b9b736474440826eb4dbc8b14921c9e47ed1fbea31f6d66d461288eca486", "bytes": 60814}
+    ],
+    "examples": [
+      {"path": "examples/audit-events.sexp", "sha256": "ad38f25f891e456e20f827129413e041f2fb4b15909ad465a55369e23968cd38", "bytes": 1933},
+      {"path": "examples/chat-server.ss", "sha256": "2e2f95dd959ade362999e4467789e58ac1f74daa38a8162a7dbf99ea7cd5a5ed", "bytes": 4993},
+      {"path": "examples/cli-tool.ss", "sha256": "a04adc7da7a1b47c916134ef4e3b501172d258460e34f908acd986dfc24649f5", "bytes": 5566},
+      {"path": "examples/data-pipeline.ss", "sha256": "37b9082c208ffbc513433e506e384cdb1caeb06229e2724de099bc0625918160", "bytes": 5599},
+      {"path": "examples/hello-api.ss", "sha256": "86e2d494bc5d0e155db6d6bb302de3fba5fc3a2254f9e3aaa730485dc7fbfa16", "bytes": 3894}
+    ],
+    "assets": [
+      {"path": "assets/jerboa-logo.png", "sha256": "7603e424cdb8c0993e65757b9b1c978970583530d4e89516acb6dd7bf6ea906b", "bytes": 247280},
+      {"path": "assets/jerboa-logo.svg", "sha256": "9d3071e88a2dc92ebe15346ccf56b4a86b05986285ab15c816946be218e6128d", "bytes": 5743}
+    ]
+  }
+}
diff --git a/docs/site-sync.md b/docs/site-sync.md
new file mode 100644
index 0000000..8485560
--- /dev/null
+++ b/docs/site-sync.md
@@ -0,0 +1,72 @@
+# Site Sync
+
+`jerboa-site` keeps the private web presentation for `jerboa.sh`.
+Canonical language, runtime, package, API, and security facts come from
+the public `jerboa` repository.
+
+## Source Split
+
+`~/mine/jerboa` owns:
+
+- canonical docs in `docs/`
+- source examples in `examples/`
+- machine-readable data in `data/`
+- public logo and brand assets in `assets/`
+
+This repository owns:
+
+- page layout and visual design
+- the Jerboa web entry point
+- generated snapshots imported from a specific Jerboa commit
+- the build artifact used by deployment
+
+The generated snapshot lives at `data/jerboa-source.json`. Its
+`repo.commit` field is the exact Jerboa revision represented by the
+site. The snapshot is generated from Jerboa's committed `HEAD` tree; if
+the local checkout had uncommitted changes at sync time, the
+`repo.working_tree_dirty` field records that.
+
+## Updating From Jerboa
+
+Preview the metadata that would be imported:
+
+```sh
+tools/sync-jerboa --jerboa ~/mine/jerboa --dry-run
+```
+
+Write the site snapshot:
+
+```sh
+tools/sync-jerboa --jerboa ~/mine/jerboa --write
+```
+
+Or use the Makefile target:
+
+```sh
+make sync-jerboa
+```
+
+Then review and build:
+
+```sh
+git diff
+make binary
+```
+
+Commit the site sync separately from the Jerboa source change. This
+makes the site pin explicit and easy to audit.
+
+## LLM Use
+
+LLM output is allowed for drafted summaries, homepage highlights, and
+release prose, but not for extracted facts. The safe flow is:
+
+1. Generate `data/jerboa-source.json`.
+2. Give the LLM only bounded diffs, selected canonical docs, and the
+   generated metadata.
+3. Require source paths or commit ids for generated claims.
+4. Review the rendered diff before committing.
+5. Deploy only after normal local verification.
+
+Do not let automated prose replace generated module, package, or API
+data.
diff --git a/tools/sync-jerboa b/tools/sync-jerboa
new file mode 100755
index 0000000..2d0927a
--- /dev/null
+++ b/tools/sync-jerboa
@@ -0,0 +1,89 @@
+#!/bin/sh
+set -eu
+
+usage() {
+  cat <<'USAGE'
+usage: tools/sync-jerboa [--jerboa PATH] [--dry-run|--write] [--commit]
+
+Import the generated Jerboa metadata snapshot into this site.
+
+Options:
+  --jerboa PATH  Jerboa repository path. Defaults to $JERBOA_HOME or ~/mine/jerboa.
+  --dry-run      Print the pending metadata diff. This is the default.
+  --write        Write data/jerboa-source.json.
+  --commit       Write and commit the generated snapshot.
+USAGE
+}
+
+jerboa=${JERBOA_HOME:-"$HOME/mine/jerboa"}
+write=0
+commit=0
+
+while [ "$#" -gt 0 ]; do
+  case "$1" in
+    --jerboa)
+      shift
+      if [ "$#" -eq 0 ]; then
+        echo "sync-jerboa: --jerboa requires a path" >&2
+        exit 2
+      fi
+      jerboa=$1
+      ;;
+    --dry-run)
+      write=0
+      commit=0
+      ;;
+    --write)
+      write=1
+      ;;
+    --commit)
+      write=1
+      commit=1
+      ;;
+    -h|--help)
+      usage
+      exit 0
+      ;;
+    *)
+      echo "sync-jerboa: unknown argument: $1" >&2
+      usage >&2
+      exit 2
+      ;;
+  esac
+  shift
+done
+
+if [ ! -x "$jerboa/tools/emit-site-data" ]; then
+  echo "sync-jerboa: missing executable $jerboa/tools/emit-site-data" >&2
+  exit 1
+fi
+
+tmp_dir=$(mktemp -d "${TMPDIR:-/tmp}/jerboa-site-sync.XXXXXX")
+trap 'rm -rf "$tmp_dir"' EXIT HUP INT TERM
+
+"$jerboa/tools/emit-site-data" --out-dir "$tmp_dir"
+snapshot=$tmp_dir/jerboa-source.json
+target=data/jerboa-source.json
+
+if [ "$write" -eq 0 ]; then
+  if [ -f "$target" ]; then
+    diff -u "$target" "$snapshot" || true
+  else
+    cat "$snapshot"
+  fi
+  exit 0
+fi
+
+mkdir -p data
+cp "$snapshot" "$target"
+echo "synced $target from $jerboa"
+
+if [ "$commit" -eq 1 ]; then
+  short_commit=$(git -C "$jerboa" rev-parse --short HEAD 2>/dev/null || printf unknown)
+  git add "$target"
+  if git diff --cached --quiet -- "$target"; then
+    echo "no site metadata changes to commit"
+    exit 0
+  fi
+  git commit -m "Sync Jerboa site metadata from $short_commit"
+fi