Wiki · The Reader and its Deploy Bundle
The reader
How to read this page
A Plain and a Clear version of this page have not been written yet. What follows is the document itself.
Precise — the source document
This is the document. Rendered from the repository at the commit above, with nothing rewritten for the web. A gate re-renders it on every deploy and fails the build if a single byte differs.
A read-only reader for this repository, built from this repository.
python reader/build.py
python -m http.server -d reader/dist 8080
Then open http://localhost:8080/. That is the whole thing. Python 3 standard library only: no pip, no npm, no CDN, no webfont, no analytics, no vendored dependency, and nothing fetched from the network at build time or at page-view time. The built pages also open directly from the filesystem — every link is relative.
Build output goes to reader/dist/, which is gitignored. The repo is the
source of truth; the rendered site is never it. Delete reader/dist/ and
rebuild whenever you want; nothing is lost, because nothing lives there.
What it renders
| Chapters | encyclopedia/**/*.md and cookbook/**/*.md |
| Ledgers | encyclopedia/CLAIM-LEDGER.md (UNI fence), encyclopedia/NATURE-LEDGER.md (NATURA classes) |
| Registers | lexicon/CONCEPTS.json + lexicon/terms/*.json → five register pages |
| Plates | reader/plates/PL-*.svg + PL-*.json |
| Apparatus | front page, A-Z index, concordance, colophon |
At the last build: 78 chapters, 10 plates, 918 rows of ## The numbers
tables, 130 lexicon concepts registered, 43 entries authored, across
98 files.
Everything in the repo that is not rendered is listed on
/colophon.html with its sha256 and the reason. Nothing is skipped silently —
a silent omission would break the claim that this is the real, as-is repo.
The invariants, and how to falsify each
Each is structural — a property of what the code can do, not a promise in a document.
1. Read-only. build.py has exactly one write door (_write_bytes), and
it resolves the target and refuses any path not inside reader/dist/. Reads
go through read_bytes(), mode 'rb'. No database, no server, no form, no
API, no edit affordance. Falsify it: run
reader/test_reader.py::test_readonly_invariant, or monkeypatch builtins.open
and rebuild and assert every write-mode call lands in dist/, or simply
git status across a build.
2. ASCII-only output. Every page is encoded xmlcharrefreplace, and
_write_bytes re-checks every byte before it lands. Devanagari, IAST, Greek
and em-dashes all leave as numeric character references. This is not cosmetic:
the fleet's MCP os_file_write silently corrupts multi-byte UTF-8 — drops
bytes, returns ok=true with a wrong sha — and it is the only file transport
this box has. ASCII-only bytes make that bug unable to fire. Falsify it: any
emitted byte >= 0x80.
3. Provenance or no page. Every page carries source path + sha256 + commit
- built UTC.
build_provenance()raises rather than half-stamp, andtemplates.render_page()raisesProvenanceErrorindependently. Two locks, no flag to open either. Falsify it: a page missing any of the four.
4. No external network references. fence_scan() greps every emitted page
for remote asset references and fails the build on a hit. Falsify it: load
any page with the network off. Anything that fails to render refutes this.
5. The repo is the truth. Every page renders bytes read from its repo file at build time. Nothing is cached, hand-copied, or carried between builds. Falsify it: the procedure below.
The test suite
python reader/test_reader.py # 75 tests, stdlib unittest, ~11s
python reader/test_reader.py -v # each test names what it observes
python reader/test_reader.py TestReadOnlyInvariant
It runs ONE real build against the real repo (nothing is mocked, the corpus is
not copied) with every mutation door instrumented — open() in a write mode,
rmtree, remove, rename, mkdir — and asserts on the record. It also
sha256s the whole repo before and after and requires the bytes not to move.
Last observed run: 75 passed, 0 failed, against 97 pages.
Two of these tests are easy to write in a way that fails loudly while measuring the wrong thing, and both mistakes were made and are documented at the test that fixes them:
test_no_external_refsmust not grep forhttp— see gap 2 below.test_no_perfect_claimsmust not grep pages for "perfect" or "verified". Measured on the built site: 29 occurrences of "perfect", 1054 of "verified" — every one of them corpus prose, in the corpus's own voice ("nature did not build one perfect component", CN-05).provenis likewise the CLAIM-LEDGER's own legitimate vocabulary for UNI's build status. A page-wide grep fires on all of it, and the only way to make it pass is to censor the repo — breaking the one thing the reader exists to keep. So the test reads the reader's own authored strings, viaast, and the reader's own chrome. SIGNUM SIGNUM MANET: the corpus's voice and the reader's travel separately.
Every fence test carries a positive control (a deliberate violation it must catch) and, where it could fire on the innocent, a negative control (the real legitimate case it must pass — the SVG namespace, a DOI citation, a CSS comment stating the fence, the corpus's own honest prose). A fence never shown to fire is a decoration, not a fence; and a test that has never been shown to pass the innocent is a censorship engine waiting for a maintainer.
How to verify a page against the repo file it came from
Every page's footer names its source and the sha256 of the bytes that were rendered. That digest is the receipt:
python -c "import hashlib;print(hashlib.sha256(open('encyclopedia/wing-NATURA/NA-05-ratios-and-scaling-laws.md','rb').read()).hexdigest())"
Compare it to the sha256 in the footer of /chapter/na-05.html. If they
differ, the page is stale or wrong, and the page is the thing that is wrong.
The commit is context; the sha256 is the receipt
A repo commit does not mean a file's bytes are in it. At the time of
writing, lexicon/ and reader/plates/ are untracked — stamping
commit f1be794 beside a lexicon page would assert its content is in that
commit, which is false. So every page states its own git file-state next to
the commit, and says so on the page when the commit does not describe those
bytes:
| file state | what the page says |
|---|---|
clean |
the commit describes these bytes |
modified |
tracked, but modified in the working tree — the commit does not describe these bytes |
untracked |
in no commit; the sha256 is this page's only receipt |
unknown |
git was unavailable at build time; the sha256 is the only receipt |
The five registers are not five translations
English is the only register with full chapter prose. That is the honest
state and every non-English register page says so, carries a PENDING badge, and
prints its own counts, computed at build time from lexicon/:
| register | status census (at last build) |
|---|---|
| Sanskrit | CITED 1 · COINED 42 |
| Latin | CITED 1 · COINED 39 · PENDING-CITATION 3 |
| Hindi | COINED 8 · PENDING-CITATION 35 |
| Spanish | PENDING-CITATION 43 |
No chapter is machine-translated. Presenting machine output as a register would
be fabrication, so the prose is absent rather than invented. No back-translation
test in lexicon/ has been run and no lexicographer has reviewed any entry, so
every rendering is PENDING / SUB EXAMINE. Of 130 registered concepts, 43 carry
an entry in any register. Nothing in the reader is hardcoded about this: change
lexicon/ and the counts move.
Two vocabularies, never merged
The reader renders the NATURA classes (nature's observed regularities,
twelve classes, governed by wing-NATURA/NA-00) and the UNI 4-value fence
(UNI's own build status, governed by CLAIM-LEDGER.md) as visually distinct
badges that share no CSS class.
This is not decoration. hypothesized is a member of both vocabularies and
means different things in each. So badge identity can never rest on the token
text. A NATURA citation is never a UNI gate: reading Kleiber's law raises no
UNI rung. build.py asserts the two badge vocabularies stay disjoint, and a
ledger page will not render unless it declares which vocabulary it speaks.
The Markdown subset (markdown.py)
markdown.py renders the Markdown this corpus actually uses, measured
across all 90 .md files — not CommonMark in general. Every omission below is
a defect avoided, not a gap.
Supported: ATX headings (97 h1 / 857 h2 / 396 h3), fenced code (278), GFM tables (3800 rows, 244 delimiter rows, 86 with escaped pipes, 0 column mismatches), unordered lists (1942), ordered lists (668), blockquotes (1356), thematic breaks (440), code spans, strong, emphasis, links, backslash escapes.
Deliberately not supported, and why:
- 4-space indented code blocks. All 172 candidate lines in this corpus are
ordered-list continuation lines (e.g.
cookbook/recipes/L12-creativity-awareness.md:104-108). Implementing indented code would render that prose as code. $...$/$$...$$TeX math. There is zero TeX math in this corpus. All 20 lines containing$are shell variables ($env:UNI_MCP,deploy/README.md:93) or money ($200inCLAIM-LEDGER.md:311,$3,450at:380). A$math rule would eat the ledger's own numbers. Display math here is written as fenced code with Unicode and renders as such — correctly, and with nothing to fetch.- Setext headings. Zero in the corpus; all 90 files open with an ATX
#. Supporting them would make the 440---thematic breaks ambiguous. - Raw HTML passthrough. There is no flag for this. See below.
- Images, reference links, autolinks, strikethrough. Zero in the corpus.
Raw HTML is escaped, always — that is fidelity, not only security
The corpus contains angle-bracket text that is prose, not markup: <sha>
(cookbook/MASTER-PLAN.md:821) and <r̄> (encyclopedia/NATURE-LEDGER.md:464).
Passing HTML through would delete those words from the page — the browser
would eat them as unknown tags. Escaping is what makes the page faithful to the
repo; it also means this renderer cannot emit an injected script.
Math degrades honestly or not at all
No math engine is vendored, so none is faked. A fenced block marked math,
tex or latex renders as labelled TeX source in a <pre class="math-src">
with a caption saying it is not typeset. The corpus currently contains no such
block; the path exists so that adding one degrades to honest source rather than
to a silent CDN fetch.
The ctx contract
build.py is the producer; reader/templates.py owns
render_page(kind, ctx) -> str and documents the contract, including a
machine-readable templates.CONTRACT. Where the two disagreed, templates.py
won and build.py changed. Read templates.py's module docstring for the
authoritative field list. The parts most easily got wrong:
provenance.built_utc— notbuild_utc.hrefis emitted verbatim;rootis used only fortheme.css. So every href must already be page-relative (../ledger/x.htmlfrom insidechapter/).build.pycomputes this withroot_for(url).kind='ledger'requiresvocabularyin('uni','natura')or it raisesVocabularyError— the sovereignty guard.kind='plate'requiresplate_id; passsvg_markup(inline), never an<img>— an<img>-embedded SVG cannot see the page'sdata-theme.- A missing chapter is
pending=True+pending_reason, andsha256may then be omitted. Missing provenance raises. These are different things.
Contract gaps — CLOSED 2026-07-16, each with the test that holds it shut
These were found by building the site and reading the emitted bytes, not by
reading the source. Each is now fixed and each fix carries a named falsifier in
test_reader.py, so it cannot silently come back.
CLOSED. templates now acceptsnumbersrows:units/scopeescaped as plain text.units_html/scope_html(the plain keys still work and are still escaped);build.pyrenders both throughmd.render_inline. A real cell —**ideal** regular tetrahedron— showed its asterisks whilevaluebeside it rendered. Falsifier:test_numbers_cells_render_their_markdown.CLOSED. The table is suppressed whenkind='ledger'withrows=[]: an empty table reading "PENDING: no rows were passed to this ledger".intro_htmlcarries the document; an honest PENDING remains for the case where there is genuinely nothing. It printed PENDING directly beneath the fully-rendered ledger — lying in the modest direction, which is still lying, and it teaches a reader to discount every other PENDING on the site. Falsifier:test_ledger_does_not_claim_a_false_pending.Plate numbers:CLOSED. Every chapter plate captioned itselfbuild.pypassednumber='PL-01';_roman()int()s it, failed, returned''.Plate .with no number.load_plates()now assigns the ordinal once, and the plate page and every citing chapter print the same one. Falsifier:test_plate_numbers_render.PlateCLOSED. 47 Python dict reprs —source_chapters:esc()'d a dict.{'href': '../chapter/na-04.html', 'label': ...}— were the visible text of the "Drawn from" marginal on every plate page. templates now renders{href,label}as a link and{label}as a path. Falsifier:test_plate_source_chapters_are_links,test_no_python_repr_reached_a_page.Table wrappers:CLOSED. No rule matched, so all ~3800 corpus table rows rendered with nomarkdown.pyemittedclass="table-wrap";theme.cssstyles only.tablewrap.overflow-xcontainer and a wide table pushed the whole page body into a horizontal scroll. Falsifier:test_table_wrap_class_matches_the_stylesheet.Lexicon status census:CLOSED. The one table stating the honest shape of the whole lexicon never rendered on any page. Falsifier:build.pypassed flat counts; templates readscounts['status_census'].test_register_counts_are_counted_not_claimed.markdown.pyhad no link-scheme fence. CLOSED (latent, never fired). The corpus contains zerojavascript:/data:links — measured — so nothing was wrong on the page. But the fence was "no author has typed one yet", which is luck, not a fence. A refused scheme now renders as inert text carrying its own words; refusing to execute a thing is not a licence to delete it. Falsifier:test_link_scheme_fence,test_safe_schemes_still_work.
Known contract gaps (open, and honest about it)
- The provenance footer says a page "renders the repository file named
above, as it stood at that commit." That is true only for a tracked,
unmodified file.
build.pycompensates by appending the file-state qualifier to thecommitstring itself, so the correction travels on the page. Fix: let templates renderprovenance.git_statedirectly. Held meanwhile bytest_provenance_commit_does_not_overclaim, which fails if an untracked or modified file is ever stamped without its qualifier. test_no_external_refsmust not grep for the bare substringhttp. Two legitimate cases exist:xmlns="http://www.w3.org/2000/svg"on every inlined plate (an XML namespace identifier, never dereferenced) and<a href="https://doi.org/...">citations (user-initiated navigation, not an asset fetch). Test for fetching constructs:src=,<link href=,url(,@importpointing athttp(s).build.py's ownfence_scan()does exactly this, and scopes@import/url()to style contexts with comments stripped — because bothPL-01.jsonandtheme.cssdescribe the fence in prose ("no@import"), and an unscoped grep fires on the promise instead of the violation.
Build notes are content, not noise
build.py reports what it observed about the corpus rather than swallowing it.
All of it lands on /colophon.html. At the last build, 52 notes, including:
url-collision—encyclopedia/00-INDEX.mdandcookbook/00-INDEX.mdboth slug to00-index, as do the twoMASTER-PLAN.mdfiles. Every member of a colliding group gets a section-qualified url, so no chapter is overwritten and urls do not depend on walk order.emit()refuses a duplicate write outright: an earlier build reported 98 pages written into 95 files while three chapters vanished with no error.unregistered-class— a## The numbersClass cell naming none of NA-00's twelve registered classes:as above(a back-reference), and**NEGATIVE (framing superseded)**—NEGATIVEis deliberately not a NATURA class (NA-00 says registering it as one would put a UNI ledger state into nature's vocabulary). The cell is carried verbatim and rendered as-is, never dropped and never invented into a class. This is NA-00's own falsifier #2 kept from firing silently.numbers-table-shape— a## The numberstable whose headers do not match the corpus convention. Headers are normalised, not exact-matched: of the 23 tables, 21 useSymbol|Value|Units|Scope|Class|Source|Falsifier,NA-01usesScope (where it holds), andNA-03uses lowercase. Exact matching would silently drop two chapters' numbers.
Structure
| file | owner | what it is |
|---|---|---|
build.py |
this reader | the generator: repo walk, provenance, ASCII emission, dist/ |
markdown.py |
this reader | the dependency-free Markdown subset renderer |
templates.py |
separate | render_page(kind, ctx) -> str, the page shells |
theme.css |
separate | the print-encyclopedia stylesheet (light + dark + print) |
test_reader.py |
separate | the invariant tests |
plates/ |
separate | 10 engraved reference plates (SVG + JSON) |
Honest state
- The chapters have no front matter. Measured: 0 of 90 files open with
---. Chapter metadata is derived from the document's own structure — the H1 is the title, and a leading blockquote is the abstract. Only 38 of 90 chapters have one; the rest render without an abstract rather than with a fabricated summary. - The reader ships no search box.
/search.htmlis a static concordance — every heading of every page, printed, so the browser's own find works offline. templates.pyships a small inline theme-toggle script and an offline filter. Both are local and fetch nothing. The plates are held to the stricter design-only fence:build.pyverifies each SVG for<script>,on*handlers,@import, remoteurl()/href, and<foreignObject>on the bytes, and refuses to inline any plate that trips it — rather than trusting thedesign_fenceblock in the plate's own JSON, which is the plate's narrative about itself.- The reader renders the corpus. It states no claim of its own about nature or about UNI, and it raises no rung.
sha256 45bafa2017c1b78f — of the original file, so what was ingested stays checkable.