From 1d3c20412e1aa53c1336d8782676f6899c332985 Mon Sep 17 00:00:00 2001
From: TimeLordRaps
Date: Mon, 12 Oct 2026 23:19:19 -0700
Subject: [PATCH 1/3] feat: make the proofsite legible and provenance-first
---
.github/workflows/pages.yml | 6 +-
README.md | 41 ++-
docs/REFERENCE.md | 23 +-
hosted/overlays/README.md | 6 +
hosted/overlays/manifest.json | 37 ++-
hosted/overlays/oai.l4del | 8 +-
hosted/overlays/tpil.l4del | 90 -------
.../src/lean4_explainer_corpora/extension.py | 5 +
.../src/lean4_explainer_corpora/overlays.py | 32 ++-
.../src/lean4_explainer_corpora/showcase.py | 233 +++++++++++++++-
packages/corpora/tests/test_cli_corpora.py | 8 +-
packages/corpora/tests/test_corpora.py | 4 +-
.../corpora/tests/test_hosted_overlays.py | 6 +-
packages/corpora/tests/test_showcase.py | 45 +++-
.../lean4_explainer_ecosystem/extension.py | 6 +-
.../lean4_explainer_implications/extension.py | 21 +-
packages/l4del/README.md | 8 +-
packages/l4del/pyproject.toml | 4 +-
.../src/lean4_explainer_l4del/__init__.py | 2 +-
.../src/lean4_explainer_l4del/extension.py | 196 ++++++++++++--
.../src/lean4_explainer_l4del/grammar.py | 76 +++++-
.../l4del/src/lean4_explainer_l4del/graph.py | 18 +-
.../l4del/src/lean4_explainer_l4del/model.py | 1 +
.../src/lean4_explainer_l4del/templates.py | 3 +
packages/l4del/tests/test_features.py | 56 +++-
packages/l4del/tests/test_l4del_spec.py | 54 +++-
packages/l4del/tests/test_overlay.py | 37 +++
.../src/lean4_explainer_lean/extension.py | 4 +-
.../map/src/lean4_explainer_map/extension.py | 27 +-
packages/map/src/lean4_explainer_map/map.js | 11 +
packages/map/tests/test_map.py | 4 +
.../src/lean4_explainer_reader/extension.py | 2 +
.../src/lean4_explainer_reader/reader.js | 14 +
packages/reader/tests/test_reader.py | 7 +
pyproject.toml | 4 +-
scripts/audit_hosted_overlay_grounding.py | 252 ++++++++++++++++++
scripts/check_static_site.py | 234 ++++++++++++++++
scripts/generate_documented_hosted_overlay.py | 9 +-
src/lean4_explainer/build.py | 11 +-
src/lean4_explainer/config.py | 15 ++
src/lean4_explainer/extensions.py | 11 +-
src/lean4_explainer/glossary.py | 23 +-
src/lean4_explainer/render.py | 234 +++++++++++++---
src/lean4_explainer/search.py | 17 +-
tests/test_build.py | 73 ++++-
tests/test_generate_hosted_overlay.py | 28 ++
tests/test_overlay_grounding_audit.py | 145 ++++++++++
tests/test_static_site_check.py | 88 ++++++
48 files changed, 2008 insertions(+), 231 deletions(-)
create mode 100644 scripts/audit_hosted_overlay_grounding.py
create mode 100644 scripts/check_static_site.py
create mode 100644 tests/test_generate_hosted_overlay.py
create mode 100644 tests/test_overlay_grounding_audit.py
create mode 100644 tests/test_static_site_check.py
diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml
index 544ed2c134..4c878f730d 100644
--- a/.github/workflows/pages.yml
+++ b/.github/workflows/pages.yml
@@ -27,7 +27,11 @@ jobs:
- name: Install
run: python -m pip install . packages/l4del packages/reader packages/map packages/implications packages/corpora packages/corpus-*
- name: Build the showcase
- run: lean4-explainer showcase --yes --out site --work work --jobs 4 --overlay-manifest hosted/overlays/manifest.json
+ run: lean4-explainer showcase --yes --out site --work work --jobs 4 --site-url https://timelordraps.github.io/lean4-explainer/ --overlay-manifest hosted/overlays/manifest.json
+ - name: Verify annotation grounding
+ run: python scripts/audit_hosted_overlay_grounding.py --manifest hosted/overlays/manifest.json --work work --report site/annotation-audit.json
+ - name: Validate the generated site
+ run: python scripts/check_static_site.py --site site --site-url https://timelordraps.github.io/lean4-explainer/
- name: Record build provenance
run: |
printf '{"repository_sha":"%s","built_at":"%s"}\n' "$GITHUB_SHA" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" > site/build-info.json
diff --git a/README.md b/README.md
index 390af11406..c75f4f74c9 100644
--- a/README.md
+++ b/README.md
@@ -1,16 +1,18 @@
# lean4-explainer
-**Read real, computer-checked mathematics, one step at a time.**
+**Read real Lean source, one step at a time.**
Lean 4 is a programming language where you write a math proof and the computer checks every single step. People have used it to formalize huge results, including Fermat's Last Theorem. But a pile of `.lean` files is hard to read if you have never seen one.
-lean4-explainer turns that pile into a website you can browse like a guided tour: every theorem gets a plain-English heading, the idea behind it, the steps of the proof, and links to the things it depends on.
+lean4-explainer turns that pile into a source-first website you can browse like a guided tour: declarations, comments,
+explanations when available, dependency links, and the exact pinned source stay together. The site labels what was
+checked by Lean, what was only scanned from text, and what remains an unreviewed explanatory overlay.
-**See it live:** https://timelordraps.github.io/lean4-explainer/ (eight well-known Lean projects, including the Fermat's Last Theorem project)
+**See it live:** https://timelordraps.github.io/lean4-explainer/ (eight well-known Lean projects, including the unfinished Fermat's Last Theorem formalization project)
## Who is this for?
-Someone who loves math and is curious about Lean. You do not need to know Lean, and you do not need to install it to read the site. If you have seen calculus or differential equations in high school, you have the background for most of the pages. When a word is new, look it up in the **Glossary**, linked from every page: it has every definition in the project, every type, property, symbol and tactic the Reading explains, and Lean's own words.
+Someone who loves math and is curious about Lean. You do not need to know Lean, and you do not need to install it to read the site. When a word is new, look it up in the **Glossary**, linked from every page: it has every definition in the project, every type, property, symbol and tactic the Reading explains, and Lean's own words.
It is also for people who write Lean and want others to understand it. You add short comments above your theorems, and the site does the rest.
@@ -35,14 +37,17 @@ Where a project has added explanations of its own (or we have, in a separate ove
| **Prerequisites** | What you should understand first |
| **Glossary** words | A word the explanations define links to its meaning in the Glossary |
| The **statement** and a link to the real source | The exact Lean, when you want it |
+| **Sources and attribution** links | The exact full-commit source plus primary project references; traceability and credit, not a claim of review |
+| An **Explanation legend** | Lean4 Domain Explanation Language (L4DEL), plus every acronym the explanation defines; unexpanded source abbreviations are labeled rather than guessed |
+| A linked **quotation** | Exact words kept distinct from the explainer's own prose and linked to the named source |
## How relationships show up
Math is a web of results leaning on each other. The site shows that web in five ways:
1. **The map on each declaration.** Open "Map" under any result to see what it builds on (left) and what uses it (right), two steps each way. The links are read from the code itself, so every project has them, explained or not. Every box is a link, and the map wears the project's own colours.
-2. **The project map** (`map.html`). The folders and how they use each other, and any one file with the files around it.
-3. **The learning path** (`learning-path.html`). One reading order where nothing appears before the things it needs (from L4DEL `prereq` lines).
+2. **The project map** (`map.html`). The folders and how they use each other, and any one file with the files around it. A searchable table exposes the same data without relying on the drawing.
+3. **The learning path** (`learning-path.html`). One reading order where nothing appears before the things it needs (from Lean4 Domain Explanation Language `prereq` lines).
4. **Link lists** under each declaration: Prerequisites, See also, and the results it uses.
5. **Implication edges** (`implications.html`, with the optional implications extension). Some links are not in the code at all: one result could give another even though the published proof goes a different way. An edge says so, and since that is a claim, it keeps the exact hypotheses, the conclusion, the commit it was read at, and how far it has been checked (suggested, argued in words, or proved in Lean and checked by the kernel). Edges are kept apart from the links read from the code, so the map never mixes the two.
@@ -70,9 +75,11 @@ pip install -e packages/corpora -e packages/corpus-flt
lean4-explainer showcase --out site
```
-## Write your own explanations (L4DEL)
+
-L4DEL is a tiny way to explain a theorem inside a Lean comment. Put lines like these just above it:
+## Write your own explanations with the Lean4 Domain Explanation Language (L4DEL)
+
+The Lean4 Domain Explanation Language (L4DEL) is a small, checked vocabulary for explaining a theorem inside a Lean comment. Put lines like these just above it:
```lean
-- L4DEL: title: Two odd numbers add to an even number
@@ -80,14 +87,19 @@ L4DEL is a tiny way to explain a theorem inside a Lean comment. Put lines like t
-- L4DEL: step: Write each number as 2k + 1.
-- L4DEL: step: Add them to get 2(k + m + 1).
-- L4DEL: prereq: Nat.odd_iff
+-- L4DEL: source: Exact formal source = https://github.com/OWNER/REPO/blob/FULL_COMMIT/Foo.lean#L42
+-- L4DEL: quote: Exact formal source = Two odd numbers add to an even number.
+-- L4DEL: acronym: DEL = Domain Explanation Language
theorem odd_add_odd ...
```
Lean treats them as ordinary comments, so your proofs are untouched. Run `lean4-explainer check` and it tells you what is missing or misspelled. Run `lean4-explainer annotate --file Foo.lean` and it prints a ready-to-fill template for every declaration that has no explanation.
If you do not own the project, keep the explanations in a separate **overlay** file instead of editing their source.
-The live showcase explicitly injects repository-owned overlays through a commit- and hash-bound manifest; corpus
-packages on PyPI contain no L4DEL explanation text. The details are in the [reference](docs/REFERENCE.md).
+The live showcase explicitly injects repository-owned overlays through a commit- and hash-bound manifest; every
+rendered overlay links to its exact full-commit source and corpus-level primary references. Those links make a claim
+auditable, but do not relabel AI-generated text as human-reviewed. Corpus packages on PyPI contain no hosted L4DEL
+explanation text. The details are in the [reference](docs/REFERENCE.md).
## Pick your pieces
@@ -98,7 +110,7 @@ The core is small and works alone. You add only what you want:
| A quick page of your Lean code | the core | The site from your declarations and comments |
| A plain-English reading of every declaration | `packages/reader` | The Reading: claim in words and as a typeset formula, givens, every proof step explained |
| What builds on what | `packages/map` | A map on every declaration and a project map, read from the code |
-| Titles, intuition, steps, glossary, learning path | `packages/l4del` | L4DEL |
+| Titles, intuition, steps, linked quotations, acronym legends, glossary, learning path | `packages/l4del` | Lean4 Domain Explanation Language (L4DEL) |
| Claims that one result gives another, each with its contract | `packages/implications` | Implication edges on the declarations they connect, and a page of them |
| Proof state after each tactic, and a trust badge | `packages/lean` | Needs Lean installed. It runs Lean on your files |
| `explain#Name` links, what changed between versions | `packages/ecosystem` | Links and a changes page |
@@ -132,7 +144,12 @@ Two of the example projects are tutorials, so you can read them here and then do
- The openai/math project is too big to publish twice (1.1 GB of source), so its pages fetch their file from GitHub, at the pinned commit, to make the Reading. Its glossary lists only the definitions that have a description (about 25,000 of 1.3 million).
- The glossary lists definitions, not theorems: theorems are results, and each one is on its file page.
- Doc comments are drawn as Markdown, with their formulas typeset. Tables and a few rarer forms show as written, and so does a formula whose TeX has a mistake in it.
-- The explanations on the example projects were written for this site. They are not the authors' words, and the site says that too.
+- The hosted explanations on the example projects are AI-generated and not human-reviewed. Most are mechanically
+ regenerated from source-controlled comments or formalization notes; the smaller curated layer is target-checked and
+ source-linked but remains unreviewed. The site says this beside every annotation and never presents it as mathematical authority.
+- The openai/math card credits the repository's internal OpenAI model for producing its manuscripts and proof artifacts,
+ while explicitly recognizing that the collection builds on the accumulated work of the mathematics community. Individual
+ scholarly credit follows each manuscript's BibTeX and bibliography, not the site owner's name.
- A few kinds of declaration (indented ones, ones made by macros) are not listed.
## More
diff --git a/docs/REFERENCE.md b/docs/REFERENCE.md
index e989e569fb..2257195394 100644
--- a/docs/REFERENCE.md
+++ b/docs/REFERENCE.md
@@ -67,9 +67,11 @@ Formulas stay as their TeX, inside ``, for the reader extensio
- `sorry` detection is textual. It does not follow dependencies and does not check axioms.
- Without the Lean extension, Lean is not run, so axiom and trust checks are UNKNOWN.
-## L4DEL: write your own explanations
+
-L4DEL is an optional extension. It reads `-- L4DEL: key: value` line comments and adds an explanation layer to the site. The core never needs it, and a project that writes none gets the same output as without it.
+## Lean4 Domain Explanation Language (L4DEL): write your own explanations
+
+The Lean4 Domain Explanation Language (L4DEL) is an optional extension. It reads `-- L4DEL: key: value` line comments and adds an explanation layer to the site. The core never needs it, and a project that writes none gets the same output as without it.
```
pip install lean4-explainer[l4del]
@@ -82,7 +84,7 @@ Whenever a declaration has no valid L4DEL, the tool says what is missing and pri
3. **Paste** a template directly above its declaration, then replace every `TODO_` value.
4. **Build** with `lean4-explainer build --lean-root lean --out docs`. A `TODO_` left in a value is a warning, so the build reports it.
-The generated site gets an L4DEL guide page (`annotate.html`), its `term` lines in the glossary (`glossary.html#t0`, …), and a learning path ordered by prerequisites.
+The generated site gets a Lean4 Domain Explanation Language guide page (`annotate.html`), its `term` and `acronym` lines in the glossary, an explanation legend at the top of each annotated file page, and a learning path ordered by prerequisites. Acronym-looking text without an explicit expansion is labeled unresolved rather than guessed; this guarantee covers the explanation layer, not arbitrary identifiers in upstream Lean code.
### The directive grammar
@@ -98,6 +100,9 @@ Each directive is one line comment:
| `prereq` | comma-separated fully qualified names the proof depends on | a repeated name is an error |
| `see` | comma-separated related declarations | a repeated name is an error |
| `term` | `word = definition` | each word is defined once in the project |
+| `acronym` | `SHORT = expansion` | one consistent expansion across the project; repeated in affected page legends |
+| `source` | `label = https://URL` | repeatable; each URL is unique |
+| `quote` | `source label = exact excerpt` | repeatable; label must match a `source`, or the hosted overlay's pinned Lean source |
Rules:
@@ -106,6 +111,8 @@ Rules:
- A directive attaches to the next declaration. Only blank lines, comments, attribute lines, and `set_option` lines may separate them. Other code makes a directive an orphan, which is an error.
- Anonymous declarations (`example`, unnamed `instance`) cannot carry directives.
- `prereq` and `see` names are exact. `foo` does not match `Demo.foo`. A name that matches nothing is a warning.
+- `source` links appear beside the explanation under **Sources and attribution**. Use a full-commit GitHub blob URL, DOI, or versioned arXiv URL where possible; a citation makes the wording traceable but does not make it reviewed or correct.
+- `quote` keeps exact source words visually and semantically separate from `intuition` and `step` prose. Its source label becomes the quotation link.
- A name with distinct definitions is ambiguous: a plain reference is an error, and it must be written `Name@path`, for example `Demo.value@Var/B.lean`.
- Values are trimmed, must not be empty, and are at most 2000 characters.
- The complete grammar is in `packages/l4del/src/lean4_explainer_l4del/grammar.py`.
@@ -172,11 +179,11 @@ Licenses are those of each repository's `LICENSE` file at the pinned commit. Whe
```
lean4-explainer sources [NAME...] # installed corpora, pinned commits, licenses
lean4-explainer fetch tutorials --yes # download only
-lean4-explainer showcase --yes --out site # download and build one subsite each under a landing page
+lean4-explainer showcase --yes --out site --site-url https://example.org/base/ # build a public, canonical site
lean4-explainer # same as showcase; prints what it will download, asks first
```
-Without `--yes`, the commands print the plan and ask. When stdin is not a terminal they download nothing and exit with code 2. Corpus packages do not select or contain L4DEL overlays, so an ordinary showcase remains annotation-free even when the L4DEL tooling is installed. A hosted build may opt in with `--overlay-manifest PATH`; every entry is checked against the corpus repository, exact commit and overlay SHA-256, and its review provenance is shown on the landing page and beside rendered annotations. The showcase landing page shows each project's commit, Lean toolchain, `mathlib` commit, and license, and the license text is copied next to each subsite. Each corpus's build errors are printed (and its first warnings): an overlay or an implication edge that names a declaration the site does not have is an error, and the command then exits with code 1, so the Pages workflow does not publish a broken site. Output is about 975 MB for all eight (measured 2026-10-10 with the reader, the map, L4DEL and the implications extension), mostly openai/math, which gets one source link per file instead of one per declaration, no map, and a glossary of its described definitions only, to stay under the 1 GB Pages limit.
+Without `--yes`, the commands print the plan and ask. When stdin is not a terminal they download nothing and exit with code 2. Corpus packages do not select or contain L4DEL overlays, so an ordinary showcase remains annotation-free even when the L4DEL tooling is installed. A hosted build may opt in with `--overlay-manifest PATH`; every entry is checked against the corpus repository, exact commit and overlay SHA-256, and its review provenance is shown on the landing page and beside rendered annotations. The hosted workflow also audits generated wording against pinned source-controlled comments or notes and publishes the machine-readable result as `annotation-audit.json`; curated wording is enumerated there as `source-linked-unreviewed`, not silently promoted to reviewed. The showcase landing page shows each project's commit, Lean toolchain, `mathlib` commit, and license, and the license text is copied next to each subsite. `--site-url` enables canonical and social metadata, a custom 404 page, `robots.txt`, and chunked sitemaps. Each corpus's build errors are printed (and its first warnings): an overlay or an implication edge that names a declaration the site does not have is an error, and the command then exits with code 1, so the Pages workflow does not publish a broken site. Output must remain below the configured byte budget; the workflow and release smoke check measure the generated artifact rather than relying on an older estimate.
## Extensions
@@ -194,6 +201,10 @@ Pass `--config lean4-explainer.toml` to `build`. Unknown keys and wrong types ar
- `source_links`: `"declaration"` (default, one link per declaration) or `"file"` (one per page, for very large trees).
- `search`, `cross_links`: booleans that switch those features on or off.
- `home`: relative URL of a landing page above this site. Adds an "All corpora" link to every page.
+- `site_url`: canonical HTTPS base URL, without a query or fragment. Every page gets a canonical URL; navigation, feature, and annotated pages also get Open Graph/Twitter metadata. Unannotated file pages use a compact shell so very large corpora remain deployable.
+- `description`: page description used in metadata.
+- `project_url`: HTTPS source-project link shown in full page footers.
+- `report_url`: HTTPS feedback or issue link shown in full page footers.
- `byte_budget`: the build fails if the output exceeds this many bytes. The default is 1 GB, the GitHub Pages limit.
- `glossary`: which of the project's definitions the glossary lists: `"all"` (default), `"described"` (only those with a doc comment, a plain comment or an extension's note; the showcase uses this for openai/math) or `"none"`.
- `imports`: where file pages list imports: `"all"` (default), `"import-only"` (only on files that do nothing but import; the showcase uses this for openai/math, whose 336,022 imports would not fit) or `"none"`.
@@ -206,7 +217,7 @@ Pass `--config lean4-explainer.toml` to `build`. Unknown keys and wrong types ar
- **Defined in this project**: every `def`, `abbrev`, `structure`, `class`, `class inductive`, `inductive` and `opaque` (theorems, instances and examples are results, not words), under its own name (the last part of the full name), with the first paragraph of its description, or its declaration line when it has none, and a link to it. Identical copies in several files are one entry. More than 2,000 entries go on one page per first letter, `glossary-a.html` … `glossary-other.html`; up to that, a long list gets letter headings. The entries come from the incremental cache, so a rebuild that reuses every file still lists them all.
- One section per extension that adds words (the `glossary` hook): L4DEL's `term` lines (the term links in explanations point here), and the reader's types, structures, properties, symbols and tactics.
- **Lean words**: the language itself (`theorem`, `sorry`, `:=`, `namespace`, `⟨ ⟩`, `·`, …), including all of the README's "Words you will meet".
-- Extension pages. L4DEL adds `annotate.html` and `learning-path.html`. The map extension (`packages/map`) adds `map.html`, with the project's folders and any one file with the files around it. It also gives every declaration a "Map" of what it builds on and what uses it, two steps each way, drawn when opened. Its links are read from the code by Lean's name rules (namespaces, `open`, imports, local names; see `packages/map/README.md`), and L4DEL `prereq` links are added as dashed arrows. Imports are followed through files that only import (from the core's import graph): in formal-conjectures that more than doubled the links, from 6,077 to 12,707. Set `map_dependencies = false` to leave it out.
+- Extension pages. L4DEL adds `annotate.html` and `learning-path.html`. The map extension (`packages/map`) adds `map.html`, with the project's folders and any one file with the files around it. The same relationships also appear in a searchable file table and a collapsible text representation, so map navigation does not require the diagram or a pointer device. It gives every declaration a "Map" of what it builds on and what uses it, two steps each way, drawn when opened. Its links are read from the code by Lean's name rules (namespaces, `open`, imports, local names; see `packages/map/README.md`), and L4DEL `prereq` links are added as dashed arrows. Imports are followed through files that only import (from the core's import graph): in formal-conjectures that more than doubled the links, from 6,077 to 12,707. Set `map_dependencies = false` to leave it out.
- Pages that have not changed since the last build are reused. The cache lives beside the output directory, outside the published site, and can be moved with `--cache`. It is loaded through an allow-list of this package's own types and the types extensions declare. A cache file that refers to anything else is ignored and rebuilt, never run.
## Development
diff --git a/hosted/overlays/README.md b/hosted/overlays/README.md
index 042db6cda9..d298c33d91 100644
--- a/hosted/overlays/README.md
+++ b/hosted/overlays/README.md
@@ -8,6 +8,12 @@ The build fails if any binding drifts. `ai-generated-unreviewed` overlays are vi
the landing page and beside every rendered annotation. Change that provenance only after an identified human has
reviewed the mathematical explanation, and record that identity in `reviewed_by`.
+Every rendered overlay annotation links directly to the declaration in the immutable, full-commit GitHub source URL.
+The manifest also supplies at least two corpus-level primary references (normally pinned project documentation plus a
+paper, blueprint, catalogue, or textbook). Commit-pinned GitHub blobs are used instead of mutable branch links or an
+on-demand Wayback snapshot: the URL names the exact bytes the site built, while the manifest hash makes drift fail
+closed. These links establish traceability; they do **not** turn an AI-generated explanation into a human review.
+
The large generated sections copy or condense only material already present in the pinned source: declaration doc
comments, immediately leading source comments, and (for openai/math) formalization notes plus Comparator configs.
They do not infer mathematics from names. Existing curated entries remain separate at the top of each file and win
diff --git a/hosted/overlays/manifest.json b/hosted/overlays/manifest.json
index e8b6f0b54c..f787430472 100644
--- a/hosted/overlays/manifest.json
+++ b/hosted/overlays/manifest.json
@@ -5,6 +5,10 @@
"file": "formal-conjectures.l4del",
"repo": "google-deepmind/formal-conjectures",
"commit": "1646ca16afd6cc7a693d3bdc9f066c4d3cc01a89",
+ "sources": [
+ {"label": "Pinned project documentation", "url": "https://github.com/google-deepmind/formal-conjectures/blob/1646ca16afd6cc7a693d3bdc9f066c4d3cc01a89/README.md"},
+ {"label": "Formal Conjectures paper, arXiv v1", "url": "https://arxiv.org/abs/2605.13171v1"}
+ ],
"sha256": "206fbdd02bb6f55040c0c6aa3173da79510d9f26b39f8519314886a8cd5f23b5",
"provenance": "ai-generated-unreviewed"
},
@@ -12,6 +16,10 @@
"file": "carleson.l4del",
"repo": "fpvandoorn/carleson",
"commit": "8972acdfd953c63bb2501ace20d61ced7c0697ec",
+ "sources": [
+ {"label": "Pinned project documentation", "url": "https://github.com/fpvandoorn/carleson/blob/8972acdfd953c63bb2501ace20d61ced7c0697ec/README.md"},
+ {"label": "Pinned Carleson blueprint", "url": "https://github.com/fpvandoorn/carleson/blob/8972acdfd953c63bb2501ace20d61ced7c0697ec/blueprint/src/chapter/main.tex"}
+ ],
"sha256": "d419881f368932b5acc53d27296da73efe5e9e684a3b52dfa934b5c62728402c",
"provenance": "ai-generated-unreviewed"
},
@@ -19,6 +27,10 @@
"file": "flt.l4del",
"repo": "ImperialCollegeLondon/FLT",
"commit": "f6c6a2fe9796d6d89cd2a0d24e8a453729d881ff",
+ "sources": [
+ {"label": "Pinned project documentation", "url": "https://github.com/ImperialCollegeLondon/FLT/blob/f6c6a2fe9796d6d89cd2a0d24e8a453729d881ff/README.md"},
+ {"label": "Pinned FLT blueprint introduction", "url": "https://github.com/ImperialCollegeLondon/FLT/blob/f6c6a2fe9796d6d89cd2a0d24e8a453729d881ff/blueprint/src/chapter/ch01introduction.tex"}
+ ],
"sha256": "cae45f492d97e1d2f40ffafcc6ee78b51b6cc8082759e32028b8fee001173e67",
"provenance": "ai-generated-unreviewed"
},
@@ -26,6 +38,10 @@
"file": "pfr.l4del",
"repo": "teorth/pfr",
"commit": "ce7ac95e620afe0ae448df63477de0b29e4dcf4d",
+ "sources": [
+ {"label": "Pinned project documentation", "url": "https://github.com/teorth/pfr/blob/ce7ac95e620afe0ae448df63477de0b29e4dcf4d/README.md"},
+ {"label": "Polynomial Freiman-Ruzsa proof paper, arXiv v2", "url": "https://arxiv.org/abs/2311.05762v2"}
+ ],
"sha256": "410ce64ef99ddfed8caea0145b6c87c0a3367a18d55ea1fc85bcba81ad3b5523",
"provenance": "ai-generated-unreviewed"
},
@@ -33,6 +49,10 @@
"file": "compfiles.l4del",
"repo": "dwrensha/compfiles",
"commit": "47b19bbfe5f38dc49379a6b53008d2909c4d71c5",
+ "sources": [
+ {"label": "Pinned compfiles documentation", "url": "https://github.com/dwrensha/compfiles/blob/47b19bbfe5f38dc49379a6b53008d2909c4d71c5/README.md"},
+ {"label": "Pinned compfiles corpus index", "url": "https://github.com/dwrensha/compfiles/blob/47b19bbfe5f38dc49379a6b53008d2909c4d71c5/Compfiles.lean"}
+ ],
"sha256": "49a34abe8c01a827fb13ba43288d59f2265619e1e0814c9f374c010da88e135a",
"provenance": "ai-generated-unreviewed"
},
@@ -40,6 +60,10 @@
"file": "mil.l4del",
"repo": "leanprover-community/mathematics_in_lean",
"commit": "dd6d752fedb14082f557913c2dccb2d4851e5173",
+ "sources": [
+ {"label": "Pinned Mathematics in Lean documentation", "url": "https://github.com/leanprover-community/mathematics_in_lean/blob/dd6d752fedb14082f557913c2dccb2d4851e5173/README.md"},
+ {"label": "Pinned Mathematics in Lean textbook", "url": "https://github.com/leanprover-community/mathematics_in_lean/blob/dd6d752fedb14082f557913c2dccb2d4851e5173/mathematics_in_lean.pdf"}
+ ],
"sha256": "350a0527db91be06fc897939ee94d75020113c2b587802ffa28ac506d14df61c",
"provenance": "ai-generated-unreviewed"
},
@@ -47,14 +71,23 @@
"file": "tpil.l4del",
"repo": "leanprover/theorem_proving_in_lean4",
"commit": "4e28129fdd58037f8f5857548d5e99fe4fb0cc57",
- "sha256": "2e4f408ca1865b2b8771e6c3c49a2d2f59fa94d4efd3d809fc95fe5a0409cf29",
+ "sources": [
+ {"label": "Pinned Theorem Proving in Lean documentation", "url": "https://github.com/leanprover/theorem_proving_in_lean4/blob/4e28129fdd58037f8f5857548d5e99fe4fb0cc57/README.md"},
+ {"label": "Pinned Theorem Proving in Lean 4 book index", "url": "https://github.com/leanprover/theorem_proving_in_lean4/blob/4e28129fdd58037f8f5857548d5e99fe4fb0cc57/book/TPiL.lean"}
+ ],
+ "sha256": "769189bc70b0b0f4f30b0eab64acd940ff690ff72cd2f69efc323d49cd58e3dc",
"provenance": "ai-generated-unreviewed"
},
"oai": {
"file": "oai.l4del",
"repo": "openai/math",
"commit": "fd4aeeb2ee4fc729c18d98444fed42fd0529eeeb",
- "sha256": "00c457020aba4f1b7108c61ac939373f9dec945ec5692c1aa9345b96d1e2ed44",
+ "credit": "OpenAI's pinned repository says an unreleased internal model produced the manuscripts and supporting proof artifacts. The collection builds on the mathematics community's accumulated problems, results, language, and formal libraries; each manuscript's BibTeX and bibliography are the authority for individual scholarly credit.",
+ "sources": [
+ {"label": "Pinned openai/math documentation", "url": "https://github.com/openai/math/blob/fd4aeeb2ee4fc729c18d98444fed42fd0529eeeb/README.md"},
+ {"label": "Pinned formalization catalogue", "url": "https://github.com/openai/math/blob/fd4aeeb2ee4fc729c18d98444fed42fd0529eeeb/lean/formalization.yaml"}
+ ],
+ "sha256": "56259df037bb701500b7c952ad7cf57f51efbf8f703ba872489904539237d86d",
"provenance": "ai-generated-unreviewed"
}
}
diff --git a/hosted/overlays/oai.l4del b/hosted/overlays/oai.l4del
index c81024d06f..c3da6bb297 100644
--- a/hosted/overlays/oai.l4del
+++ b/hosted/overlays/oai.l4del
@@ -4343,7 +4343,7 @@
@file ComparatorChallenges/KLInvariance.lean
@decl OAI.KLInvariance.NormalizedKL
-- L4DEL: title: Exact equal-parameter normalization in the introduction
--- L4DEL: intuition: Exact equal-parameter normalization in the introduction. The first family is R and the second is P. `reflect d p` is `q^d p(q⁻¹)` when `p.natDegree ≤ d`, which follows here from the degree bound (or the diagonal normalization). The sum is over the real Bruhat interval; its finiteness is a separate
+-- L4DEL: intuition: Exact equal-parameter normalization in the introduction. The first family is R and the second is P. `reflect d p` is `q^d p(q⁻¹)` when `p.natDegree ≤ d`, which follows here from the degree bound (or the diagonal normalization). The sum is over the real Bruhat interval; its finiteness is a separate theorem, not a different definition of order.
-- L4DEL: step: This explanation is a condensed rendering of the declaration's own doc comment at the pinned source commit; consult the exact Lean statement below for authority.
@file ComparatorChallenges/KLInvariance.lean
@@ -5620,7 +5620,7 @@
@file ComparatorChallenges/PartitionConsistency.lean
@decl OAI.PartitionConsistency.Derives
-- L4DEL: title: Classical first-order natural deduction with equality
--- L4DEL: intuition: Classical first-order natural deduction with equality. Hypotheses may be an infinite theory, but a derivation is a finite inductive proof. The `allI` and `exE` rules use a fresh last variable, and shift all assumptions to avoid capture. We allow finitely many free variables in proofs of closed sente
+-- L4DEL: intuition: Classical first-order natural deduction with equality. Hypotheses may be an infinite theory, but a derivation is a finite inductive proof. The `allI` and `exE` rules use a fresh last variable, and shift all assumptions to avoid capture. We allow finitely many free variables in proofs of closed sentences.
-- L4DEL: step: This explanation is a condensed rendering of the declaration's own doc comment at the pinned source commit; consult the exact Lean statement below for authority.
@file ComparatorChallenges/PartitionConsistency.lean
@@ -6471,7 +6471,7 @@
@file ComparatorChallenges/RelativeGeneration.lean
@decl OAI.RelativeGeneration.MainTarget
-- L4DEL: title: Documented declaration: MainTarget
--- L4DEL: intuition: Every irreducible inclusion of concrete type II₁ factors with separable predual of the larger factor has a dense Gδ full relative-generator locus in the normalized trace two-topology. The normalized faithful normal trace is asserted to exist; no representation, transport, cyclicity, or perturbation
+-- L4DEL: intuition: Every irreducible inclusion of concrete type II₁ factors with separable predual of the larger factor has a dense Gδ full relative-generator locus in the normalized trace two-topology. The normalized faithful normal trace is asserted to exist; no representation, transport, cyclicity, or perturbation premise is imposed.
-- L4DEL: step: This explanation is a condensed rendering of the declaration's own doc comment at the pinned source commit; consult the exact Lean statement below for authority.
@file ComparatorChallenges/RelativeGeneration.lean
@@ -8186,7 +8186,7 @@
@file ComparatorChallenges/UniformKServer.lean
@decl OAI.UniformKServer.UniformAlgorithmClaim
-- L4DEL: title: Full uniform main objective in the explicit bit model, not a theorem or assumption
--- L4DEL: intuition: Full uniform main objective in the explicit bit model, not a theorem or assumption. Machine transition steps are bit operations. Installing the standard input and reading/erasing the output adds only O(L + budget) charged bit operations, hence is absorbed by a single larger polynomial. A single dete
+-- L4DEL: intuition: Full uniform main objective in the explicit bit model, not a theorem or assumption. Machine transition steps are bit operations. Installing the standard input and reading/erasing the output adds only O(L + budget) charged bit operations, hence is absorbed by a single larger polynomial. A single deterministic boot is sufficient for the source construction.
-- L4DEL: step: This explanation is a condensed rendering of the declaration's own doc comment at the pinned source commit; consult the exact Lean statement below for authority.
@file ComparatorChallenges/UniformSparsestCut.lean
diff --git a/hosted/overlays/tpil.l4del b/hosted/overlays/tpil.l4del
index d6b97832d2..680649abfc 100644
--- a/hosted/overlays/tpil.l4del
+++ b/hosted/overlays/tpil.l4del
@@ -186,12 +186,6 @@
-- L4DEL: intuition: ----
-- L4DEL: step: This explanation is a condensed rendering of the declaration's own leading source comment at the pinned source commit; consult the exact Lean statement below for authority.
-@file book/TPiL/DependentTypeTheory.lean
-@decl add
--- L4DEL: title: Documented declaration: add
--- L4DEL: intuition: ---
--- L4DEL: step: This explanation is a condensed rendering of the declaration's own leading source comment at the pinned source commit; consult the exact Lean statement below for authority.
-
@file book/TPiL/DependentTypeTheory.lean
@decl doTwice
-- L4DEL: title: Documented declaration: doTwice
@@ -366,30 +360,12 @@
-- L4DEL: intuition: ----
-- L4DEL: step: This explanation is a condensed rendering of the declaration's own leading source comment at the pinned source commit; consult the exact Lean statement below for authority.
-@file book/TPiL/InductionAndRecursion.lean
-@decl ack_gt_zero
--- L4DEL: title: Documented declaration: ack_gt_zero
--- L4DEL: intuition: -----------
--- L4DEL: step: This explanation is a condensed rendering of the declaration's own leading source comment at the pinned source commit; consult the exact Lean statement below for authority.
-
-@file book/TPiL/InductionAndRecursion.lean
-@decl bar
--- L4DEL: title: Documented declaration: bar
--- L4DEL: intuition: ------
--- L4DEL: step: This explanation is a condensed rendering of the declaration's own leading source comment at the pinned source commit; consult the exact Lean statement below for authority.
-
@file book/TPiL/InductionAndRecursion.lean
@decl f_or
-- L4DEL: title: Documented declaration: f_or
-- L4DEL: intuition: ----
-- L4DEL: step: This explanation is a condensed rendering of the declaration's own leading source comment at the pinned source commit; consult the exact Lean statement below for authority.
-@file book/TPiL/InductionAndRecursion.lean
-@decl foo
--- L4DEL: title: Documented declaration: foo
--- L4DEL: intuition: ------
--- L4DEL: step: This explanation is a condensed rendering of the declaration's own leading source comment at the pinned source commit; consult the exact Lean statement below for authority.
-
@file book/TPiL/InductionAndRecursion.lean
@decl Hidden.and
-- L4DEL: title: Documented declaration: and
@@ -414,12 +390,6 @@
-- L4DEL: intuition: ----
-- L4DEL: step: This explanation is a condensed rendering of the declaration's own leading source comment at the pinned source commit; consult the exact Lean statement below for authority.
-@file book/TPiL/InductionAndRecursion.lean
-@decl sub1
--- L4DEL: title: Documented declaration: sub1
--- L4DEL: intuition: ------
--- L4DEL: step: This explanation is a condensed rendering of the declaration's own leading source comment at the pinned source commit; consult the exact Lean statement below for authority.
-
@file book/TPiL/InductionAndRecursion.lean
@decl tail
-- L4DEL: title: Documented declaration: tail
@@ -432,12 +402,6 @@
-- L4DEL: intuition: ------
-- L4DEL: step: This explanation is a condensed rendering of the declaration's own leading source comment at the pinned source commit; consult the exact Lean statement below for authority.
-@file book/TPiL/InductionAndRecursion.lean
-@decl Vect.add
--- L4DEL: title: Documented declaration: add
--- L4DEL: intuition: ----
--- L4DEL: step: This explanation is a condensed rendering of the declaration's own leading source comment at the pinned source commit; consult the exact Lean statement below for authority.
-
@file book/TPiL/InductionAndRecursion.lean
@decl Vect.head
-- L4DEL: title: Documented declaration: head
@@ -456,12 +420,6 @@
-- L4DEL: intuition: -----
-- L4DEL: step: This explanation is a condensed rendering of the declaration's own leading source comment at the pinned source commit; consult the exact Lean statement below for authority.
-@file book/TPiL/InductionAndRecursion.lean
-@decl zero_add
--- L4DEL: title: Documented declaration: zero_add
--- L4DEL: intuition: ---
--- L4DEL: step: This explanation is a condensed rendering of the declaration's own leading source comment at the pinned source commit; consult the exact Lean statement below for authority.
-
@file book/TPiL/InductiveTypes.lean
@decl Weekday.Weekday.Hidden.and
-- L4DEL: title: Documented declaration: and
@@ -546,24 +504,12 @@
-- L4DEL: intuition: ----
-- L4DEL: step: This explanation is a condensed rendering of the declaration's own leading source comment at the pinned source commit; consult the exact Lean statement below for authority.
-@file book/TPiL/InductiveTypes.lean
-@decl Weekday.Weekday.Hidden.zero_add
--- L4DEL: title: Documented declaration: zero_add
--- L4DEL: intuition: ----
--- L4DEL: step: This explanation is a condensed rendering of the declaration's own leading source comment at the pinned source commit; consult the exact Lean statement below for authority.
-
@file book/TPiL/InductiveTypes.lean
@decl Weekday.Weekday.next_previous
-- L4DEL: title: Documented declaration: next_previous
-- L4DEL: intuition: ----
-- L4DEL: step: This explanation is a condensed rendering of the declaration's own leading source comment at the pinned source commit; consult the exact Lean statement below for authority.
-@file book/TPiL/InductiveTypes.lean
-@decl Weekday.Weekday.silly
--- L4DEL: title: Documented declaration: silly
--- L4DEL: intuition: ----
--- L4DEL: step: This explanation is a condensed rendering of the declaration's own leading source comment at the pinned source commit; consult the exact Lean statement below for authority.
-
@file book/TPiL/InteractingWithLean.lean
@decl (example at line 470)
-- L4DEL: title: Equivalent:
@@ -600,12 +546,6 @@
-- L4DEL: intuition: ----
-- L4DEL: step: This explanation is a condensed rendering of the declaration's own leading source comment at the pinned source commit; consult the exact Lean statement below for authority.
-@file book/TPiL/InteractingWithLean.lean
-@decl List.isPrefix_self
--- L4DEL: title: Documented declaration: isPrefix_self
--- L4DEL: intuition: ----
--- L4DEL: step: This explanation is a condensed rendering of the declaration's own leading source comment at the pinned source commit; consult the exact Lean statement below for authority.
-
@file book/TPiL/InteractingWithLean.lean
@decl {lit}`Nat`.(instance at line 1091)
-- L4DEL: title: Documented declaration: (instance at line 1091)
@@ -684,12 +624,6 @@
-- L4DEL: intuition: ----
-- L4DEL: step: This explanation is a condensed rendering of the declaration's own leading source comment at the pinned source commit; consult the exact Lean statement below for authority.
-@file book/TPiL/PropositionsAndProofs.lean
-@decl t1
--- L4DEL: title: Documented declaration: t1
--- L4DEL: intuition: ----
--- L4DEL: step: This explanation is a condensed rendering of the declaration's own leading source comment at the pinned source commit; consult the exact Lean statement below for authority.
-
@file book/TPiL/QuantifiersEquality.lean
@decl (example at line 1014)
-- L4DEL: title: Documented declaration: (example at line 1014)
@@ -732,18 +666,6 @@
-- L4DEL: intuition: ----
-- L4DEL: step: This explanation is a condensed rendering of the declaration's own leading source comment at the pinned source commit; consult the exact Lean statement below for authority.
-@file book/TPiL/QuantifiersEquality.lean
-@decl even_plus_even
--- L4DEL: title: Documented declaration: even_plus_even
--- L4DEL: intuition: ----
--- L4DEL: step: This explanation is a condensed rendering of the declaration's own leading source comment at the pinned source commit; consult the exact Lean statement below for authority.
-
-@file book/TPiL/QuantifiersEquality.lean
-@decl T
--- L4DEL: title: Documented declaration: T
--- L4DEL: intuition: ----
--- L4DEL: step: This explanation is a condensed rendering of the declaration's own leading source comment at the pinned source commit; consult the exact Lean statement below for authority.
-
@file book/TPiL/StructuresAndRecords.lean
@decl with.p
-- L4DEL: title: Documented declaration: p
@@ -816,12 +738,6 @@
-- L4DEL: intuition: ----
-- L4DEL: step: This explanation is a condensed rendering of the declaration's own leading source comment at the pinned source commit; consult the exact Lean statement below for authority.
-@file book/TPiL/TypeClasses.lean
-@decl Ex.Ex.Add
--- L4DEL: title: Documented declaration: Add
--- L4DEL: intuition: ----
--- L4DEL: step: This explanation is a condensed rendering of the declaration's own leading source comment at the pinned source commit; consult the exact Lean statement below for authority.
-
@file book/TPiL/TypeClasses.lean
@decl Ex.Ex.double
-- L4DEL: title: Documented declaration: double
@@ -893,10 +809,4 @@
-- L4DEL: title: Documented declaration: Morphism
-- L4DEL: intuition: ----
-- L4DEL: step: This explanation is a condensed rendering of the declaration's own leading source comment at the pinned source commit; consult the exact Lean statement below for authority.
-
-@file book/TPiL/TypeClasses.lean
-@decl Ex.Ex.s
--- L4DEL: title: Documented declaration: s
--- L4DEL: intuition: ----
--- L4DEL: step: This explanation is a condensed rendering of the declaration's own leading source comment at the pinned source commit; consult the exact Lean statement below for authority.
# END GENERATED SOURCE-COMMENT ANNOTATIONS
diff --git a/packages/corpora/src/lean4_explainer_corpora/extension.py b/packages/corpora/src/lean4_explainer_corpora/extension.py
index 25121fbe09..5fcd531568 100644
--- a/packages/corpora/src/lean4_explainer_corpora/extension.py
+++ b/packages/corpora/src/lean4_explainer_corpora/extension.py
@@ -43,6 +43,10 @@ def register_cli(self, commands: argparse._SubParsersAction) -> None:
build.add_argument("--out", type=Path, default=Path(_SITE_DEFAULT), help="output directory")
build.add_argument("--work", type=Path, default=Path(_WORK_DEFAULT), help="where checkouts are kept")
build.add_argument("--jobs", type=int, default=_DEFAULT_JOBS, help="worker processes for indexing")
+ build.add_argument(
+ "--site-url",
+ help="canonical public HTTPS URL for metadata, robots.txt and sitemaps (for example https://example.org/site/)",
+ )
build.add_argument(
"--overlay-manifest",
type=Path,
@@ -116,6 +120,7 @@ def _showcase(args) -> int:
link_out=[c for c in registry.installed().values() if c.name not in chosen],
jobs=args.jobs,
overlay_manifest=args.overlay_manifest,
+ site_url=args.site_url,
)
except (ExplainerError, OSError) as exc:
print(f"error: {exc}", file=sys.stderr)
diff --git a/packages/corpora/src/lean4_explainer_corpora/overlays.py b/packages/corpora/src/lean4_explainer_corpora/overlays.py
index 49e45a8fd2..236eca5fab 100644
--- a/packages/corpora/src/lean4_explainer_corpora/overlays.py
+++ b/packages/corpora/src/lean4_explainer_corpora/overlays.py
@@ -7,6 +7,7 @@
import re
from dataclasses import dataclass
from pathlib import Path
+from urllib.parse import urlsplit
from lean4_explainer.errors import ExplainerError
@@ -21,6 +22,10 @@
}
)
_SHA256_RE = re.compile(r"[0-9a-f]{64}")
+DEFAULT_CREDIT = (
+ "The upstream authors and contributors retain credit for the project and mathematics. "
+ "lean4-explainer only renders their pinned source and adds the separately disclosed explanation layer."
+)
@dataclass(frozen=True)
@@ -28,6 +33,8 @@ class HostedOverlay:
path: Path
provenance: str
reviewed_by: str = ""
+ sources: tuple[tuple[str, str], ...] = ()
+ credit: str = DEFAULT_CREDIT
@property
def disclosure(self) -> str:
@@ -94,5 +101,28 @@ def load_overlay_manifest(path: Path, corpora: list[Corpus]) -> dict[str, Hosted
raise ExplainerError(f"{where}: reviewed_by is required for {provenance}")
if provenance == "ai-generated-unreviewed" and reviewed_by.strip():
raise ExplainerError(f"{where}: an unreviewed overlay cannot name a reviewer")
- selected[corpus.name] = HostedOverlay(overlay, provenance, reviewed_by.strip())
+ source_rows = entry.get("sources")
+ if not isinstance(source_rows, list) or not source_rows:
+ raise ExplainerError(f"{where}: sources must be a non-empty list of stable reference links")
+ sources: list[tuple[str, str]] = []
+ for index, row in enumerate(source_rows, start=1):
+ if not isinstance(row, dict) or not isinstance(row.get("label"), str) or not row["label"].strip():
+ raise ExplainerError(f"{where}: sources[{index}] needs a non-empty label")
+ url = row.get("url")
+ parsed = urlsplit(url) if isinstance(url, str) else None
+ if not parsed or parsed.scheme != "https" or not parsed.netloc or parsed.username or parsed.password:
+ raise ExplainerError(f"{where}: sources[{index}] must be an HTTPS URL without credentials")
+ if url in {found for _, found in sources}:
+ raise ExplainerError(f"{where}: source URL repeated: {url}")
+ sources.append((row["label"].strip(), url))
+ credit = entry.get("credit", "")
+ if not isinstance(credit, str):
+ raise ExplainerError(f"{where}: credit must be a string")
+ selected[corpus.name] = HostedOverlay(
+ overlay,
+ provenance,
+ reviewed_by.strip(),
+ tuple(sources),
+ credit.strip() or DEFAULT_CREDIT,
+ )
return selected
diff --git a/packages/corpora/src/lean4_explainer_corpora/showcase.py b/packages/corpora/src/lean4_explainer_corpora/showcase.py
index eb6f3a4f69..5ba550018e 100644
--- a/packages/corpora/src/lean4_explainer_corpora/showcase.py
+++ b/packages/corpora/src/lean4_explainer_corpora/showcase.py
@@ -2,19 +2,21 @@
from __future__ import annotations
+import json
import os
import subprocess
import time
from dataclasses import dataclass
from html import escape
from pathlib import Path
+from urllib.parse import quote, urljoin, urlsplit
from lean4_explainer.build import BuildSummary, build_site
from lean4_explainer.config import PAGES_LIMIT_BYTES, Config
from lean4_explainer.errors import ExplainerError
from lean4_explainer.extensions import extensions
from lean4_explainer.lake import LakeProject, include_patterns, read_lake
-from lean4_explainer.render import ASSET_DIR, SITE_CSS
+from lean4_explainer.render import ASSET_DIR, FAVICON_SVG, SITE_CSS
from .registry import Corpus
from .overlays import HostedOverlay, load_overlay_manifest
@@ -29,12 +31,22 @@
h1 { font-size: 2.1rem; letter-spacing: 0.01em; }
ol.list { counter-reset: none; padding: 0; }
ol.list > li { list-style: none; background: var(--panel); border: 1px solid var(--line); border-left: 6px solid var(--card, var(--accent)); border-radius: 6px; padding: 0.8rem 1rem; margin-bottom: 0.8rem; }
+.start { margin: 1rem 0 1.3rem; padding: 0.9rem 1rem; border: 1px solid var(--line); border-radius: 7px; background: var(--panel); }
+.start h2 { margin-top: 0; }
+.start ul { margin-bottom: 0; }
+.card-actions { display: flex; flex-wrap: wrap; gap: 0.35rem 0.85rem; margin-top: 0.55rem; font-size: 0.92rem; }
+.card-actions a { font-weight: 600; }
""",
)
GITHUB = "https://github.com"
RAW = "https://raw.githubusercontent.com"
_GIT_TIMEOUT_SECONDS = 1800
+SITE_REPOSITORY = "https://github.com/TimeLordRaps/lean4-explainer"
+REPORT_URL = SITE_REPOSITORY + "/issues/new?title=Site%20feedback%3A%20"
+SHOWCASE_DESCRIPTION = (
+ "Explore declarations, source, grounded explanations, learning paths, and dependency maps across major Lean projects."
+)
@dataclass(frozen=True)
@@ -88,8 +100,10 @@ def build_showcase(
jobs: int = 1,
fetch=fetch_corpus,
overlay_manifest: Path | None = None,
+ site_url: str | None = None,
) -> ShowcaseResult:
"""Fetch every corpus first, so a failed download stops the run before anything is published."""
+ public_url = _site_url(site_url)
overlays = load_overlay_manifest(overlay_manifest, corpora) if overlay_manifest else {}
checkouts = [(corpus, fetch(corpus, work_dir, base_url=base_url)) for corpus in corpora]
out_dir.mkdir(parents=True, exist_ok=True)
@@ -109,6 +123,10 @@ def build_showcase(
source_links=corpus.source_links,
byte_budget=byte_budget,
home="../index.html",
+ site_url=f"{public_url}{corpus.name}/" if public_url else None,
+ description=f"Explore {corpus.title}: Lean declarations, pinned source, explanations, and dependencies.",
+ project_url=f"{GITHUB}/{corpus.repo}/tree/{corpus.commit}",
+ report_url=REPORT_URL,
glossary=corpus.glossary,
imports=corpus.imports,
options=tuple(
@@ -137,6 +155,7 @@ def build_showcase(
items.append(ShowcaseItem(corpus, summary, project, time.monotonic() - started))
(out_dir / ASSET_DIR).mkdir(parents=True, exist_ok=True)
(out_dir / ASSET_DIR / "site.css").write_text(SITE_CSS + LANDING_CSS, encoding="utf-8")
+ (out_dir / ASSET_DIR / "favicon.svg").write_text(FAVICON_SVG, encoding="utf-8")
(out_dir / ".nojekyll").write_text("", encoding="utf-8")
guide = next(
(f"{i.corpus.name}/annotate.html" for i in items if (out_dir / i.corpus.name / "annotate.html").is_file()),
@@ -145,9 +164,29 @@ def build_showcase(
annotated = set(overlays) if any(ext.name == "l4del" for ext in extensions()) else set()
curated = {i.corpus.name for i in items if (out_dir / i.corpus.name / "implications.html").is_file()}
disclosures = {name: overlay.disclosure for name, overlay in overlays.items()}
+ credits = {name: overlay.credit for name, overlay in overlays.items()}
+ source_attributions = {name: overlay.sources for name, overlay in overlays.items()}
+ actions = _corpus_actions(items, out_dir)
+ start_links = _start_links(items, actions, out_dir)
(out_dir / "index.html").write_text(
- _landing(items, link_out or [], guide, annotated, curated, disclosures), encoding="utf-8"
+ _landing(
+ items,
+ link_out or [],
+ guide,
+ annotated,
+ curated,
+ disclosures,
+ actions,
+ start_links,
+ public_url,
+ credits=credits,
+ source_attributions=source_attributions,
+ ),
+ encoding="utf-8",
)
+ (out_dir / "404.html").write_text(_not_found(public_url), encoding="utf-8")
+ if public_url:
+ _write_discovery(out_dir, public_url)
return ShowcaseResult(tuple(items), _tree_bytes(out_dir), byte_budget)
@@ -161,6 +200,54 @@ def _copy_license(checkout: Path, target: Path, lean_root: str) -> None:
return
+def _site_url(value: str | None) -> str:
+ if not value:
+ return ""
+ parsed = urlsplit(value)
+ if parsed.scheme != "https" or not parsed.netloc or parsed.query or parsed.fragment:
+ raise ExplainerError("site_url must be an HTTPS URL without a query or fragment")
+ return value.rstrip("/") + "/"
+
+
+def _corpus_actions(items: list[ShowcaseItem], out_dir: Path) -> dict[str, list[tuple[str, str]]]:
+ found: dict[str, list[tuple[str, str]]] = {}
+ for item in items:
+ name = item.corpus.name
+ root = out_dir / name
+ choices = [
+ ("Browse", "index.html"),
+ ("Search", "search.html"),
+ ("Glossary", "glossary.html"),
+ ("Learn", "learning-path.html"),
+ ("Map", "map.html"),
+ ]
+ found[name] = [(label, f"{name}/{rel}") for label, rel in choices if (root / rel).is_file()]
+ return found
+
+
+def _start_links(
+ items: list[ShowcaseItem], actions: dict[str, list[tuple[str, str]]], out_dir: Path
+) -> list[tuple[str, str, str]]:
+ links: list[tuple[str, str, str]] = []
+ titles = {item.corpus.name: item.corpus.title for item in items}
+ if (out_dir / "flt" / "FLT" / "Proof.lean.html").is_file():
+ links.append(("Read an explained theorem", "flt/FLT/Proof.lean.html#L111", "Fermat's Last Theorem and its unfinished proof boundary"))
+ if (out_dir / "oai" / "search.html").is_file():
+ links.append(("Search a concept", "oai/search.html?q=Hadwiger", "A shareable search in openai/math"))
+ for wanted, heading, detail in (
+ ("Learn", "Follow a learning path", "Declarations ordered after their prerequisites"),
+ ("Glossary", "Use a project glossary", "Definitions and explanatory vocabulary"),
+ ):
+ match = next(((name, href) for name, rows in actions.items() for label, href in rows if label == wanted), None)
+ if match:
+ name, href = match
+ links.append((heading, href, f"{detail} in {titles[name]}"))
+ if not links and items:
+ first = items[0].corpus
+ links.append((f"Browse {first.title}", f"{first.name}/index.html", "Start at the corpus index"))
+ return links[:4]
+
+
def _landing(
items: list[ShowcaseItem],
link_out: list[Corpus],
@@ -168,8 +255,17 @@ def _landing(
annotated: set[str] = frozenset(), # type: ignore[assignment]
curated: set[str] = frozenset(), # type: ignore[assignment]
disclosures: dict[str, str] | None = None,
+ actions: dict[str, list[tuple[str, str]]] | None = None,
+ start_links: list[tuple[str, str, str]] | None = None,
+ site_url: str = "",
+ credits: dict[str, str] | None = None,
+ source_attributions: dict[str, tuple[tuple[str, str], ...]] | None = None,
) -> str:
disclosures = disclosures or {}
+ actions = actions or {}
+ start_links = start_links or []
+ credits = credits or {}
+ source_attributions = source_attributions or {}
cards = []
for item in items:
corpus, summary, project = item.corpus, item.summary, item.project
@@ -180,17 +276,40 @@ def _landing(
mathlib = dict(project.pinned).get("mathlib") if project else None
if mathlib:
pins.append(f"mathlib {mathlib[:12]}")
+ corpus_actions = actions.get(corpus.name, ())
+ action_html = ""
+ if corpus_actions:
+ action_html = (
+ f'"
+ )
+ attribution = ""
+ if corpus.name in annotated:
+ links = ", ".join(
+ f'{escape(label)}'
+ for label, url in source_attributions.get(corpus.name, ())
+ )
+ attribution = (
+ f' Credit: {escape(credits.get(corpus.name, ""))}'
+ + (f' Primary project and mathematical sources: {links}.' if links else "")
+ )
cards.append(
f'
{escape(corpus.title)} '
f'({escape(corpus.kind)}) {escape(corpus.blurb)} '
f'{escape(", ".join(facts))}. {escape(", ".join(pins))}. '
- f'Source: {escape(corpus.repo)}. '
+ f'Source: {escape(corpus.repo)} at the pinned commit. '
f'License: {escape(corpus.license)} (text).'
+ (f" {escape(corpus.license_note)}" if corpus.license_note else "")
+ (
" Selected declarations are explained with L4DEL from a hosted sidecar overlay; "
"the upstream authors did not write these explanations and their sources are unchanged. "
+ "Each explanation links back to its exact pinned Lean source. "
+ escape(disclosures.get(corpus.name, ""))
+ + attribution
if corpus.name in annotated
else ""
)
@@ -201,13 +320,31 @@ def _landing(
if corpus.name in curated
else ""
)
- + "
"
+ + ""
+ + action_html
+ + ""
)
for corpus in link_out:
cards.append(
- f'
What lean4-explainer produces for the Lean projects below. Hosted annotations, when present, "
- "carry their provenance beside the explanation. "
+ "carry their provenance and source grounding beside the explanation. "
"To write your own explanations, start with the "
- + (f'L4DEL guide' if guide else "L4DEL guide")
- + ' or '
- "the README.