mirror of
https://github.com/saymrwulf/verifying-crypto-with-lean.git
synced 2026-09-03 19:53:45 +00:00
publication history: dated edition record in the front matter, machine-bound
The current-edition line (fourteen chapters, 129 pages, published 2026-08-08) is now a live claim: check-book.sh verifies its chapter and page counts against the built book AND verifies the COMMITTED main.pdf carries the line — so the PDF a GitHub visitor downloads can no longer silently lag the sources. Historical lines are exempt from the whole-book claim scans (BEGIN/END PUBHIST markers). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
311f60d4d0
commit
e2660e8e71
4 changed files with 81 additions and 6 deletions
|
|
@ -24,7 +24,9 @@ Lean 4 against models extracted from the actual Rust sources:
|
||||||
|
|
||||||
**[`main.pdf`](main.pdf)** — fourteen chapters + interlude + three
|
**[`main.pdf`](main.pdf)** — fourteen chapters + interlude + three
|
||||||
appendices, full color, built with LaTeX/TikZ from the sources in this
|
appendices, full color, built with LaTeX/TikZ from the sources in this
|
||||||
repo (`./build.sh`, tectonic, no root needed). No prior Lean or formal
|
repo (`./build.sh`, tectonic, no root needed). **Second edition,
|
||||||
|
published August 8, 2026** (129 pages; the full publication history is
|
||||||
|
printed in the book's front matter). No prior Lean or formal
|
||||||
methods assumed; high-school algebra and a little programming suffice.
|
methods assumed; high-school algebra and a little programming suffice.
|
||||||
|
|
||||||
1. **Why Verify?** — the carry bug testing cannot find
|
1. **Why Verify?** — the carry bug testing cannot find
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,13 @@ def main():
|
||||||
readme = read(os.path.join(book, "README.md"))
|
readme = read(os.path.join(book, "README.md"))
|
||||||
chdir = os.path.join(book, "chapters")
|
chdir = os.path.join(book, "chapters")
|
||||||
|
|
||||||
|
# the publication-history block: its CURRENT-edition line is bound to
|
||||||
|
# measurements below; its historical lines are frozen and exempt from
|
||||||
|
# the whole-book claim scans, so strip the span for scanning purposes
|
||||||
|
mhist = re.search(r"% BEGIN PUBHIST.*?% END PUBHIST", main_tex, re.S)
|
||||||
|
pubhist = mhist.group(0) if mhist else ""
|
||||||
|
main_scan = main_tex.replace(pubhist, "")
|
||||||
|
|
||||||
# ── Phase 0: source hygiene ────────────────────────────────────────────
|
# ── Phase 0: source hygiene ────────────────────────────────────────────
|
||||||
print("=== Phase 0: source hygiene ===")
|
print("=== Phase 0: source hygiene ===")
|
||||||
|
|
||||||
|
|
@ -128,12 +135,44 @@ def main():
|
||||||
bad = [l for l in txt.splitlines() if "??" in l]
|
bad = [l for l in txt.splitlines() if "??" in l]
|
||||||
check(not bad, "no unresolved references ('??') in rendered PDF",
|
check(not bad, "no unresolved references ('??') in rendered PDF",
|
||||||
bad[0][:60] if bad else "clean")
|
bad[0][:60] if bad else "clean")
|
||||||
page_claims = re.findall(r"(\d{2,4}) pages", readme + main_tex)
|
page_claims = re.findall(r"(\d{2,4}) pages", readme + main_scan)
|
||||||
if page_claims:
|
if page_claims:
|
||||||
for pc in page_claims:
|
for pc in page_claims:
|
||||||
check(int(pc) == pages, f"page-count claim {pc} == built {pages}")
|
check(int(pc) == pages, f"page-count claim {pc} == built {pages}")
|
||||||
else:
|
else:
|
||||||
ok("no page-count claim in prose (nothing to bind)")
|
ok("no page-count claim outside the history block (nothing to bind)")
|
||||||
|
|
||||||
|
# publication history: the current-edition line is a live claim
|
||||||
|
med = re.search(r"\\textbf\{(\w+) edition\} --- published "
|
||||||
|
r"([A-Za-z]+ \d+, \d{4}): (\w+)\s*\n?chapters, "
|
||||||
|
r"(\d+) pages", pubhist)
|
||||||
|
if not (pubhist and med):
|
||||||
|
fail("publication-history block with parseable current-edition line",
|
||||||
|
"missing" if not pubhist else "line not parseable")
|
||||||
|
else:
|
||||||
|
check(WORDS.get(med.group(3)) == n_ch,
|
||||||
|
"current edition's chapter count == measured",
|
||||||
|
f"{med.group(3)} vs {n_ch}")
|
||||||
|
check(int(med.group(4)) == pages,
|
||||||
|
"current edition's page count == built PDF",
|
||||||
|
f"{med.group(4)} vs {pages}")
|
||||||
|
# committed PDF must carry the current-edition line: this is what
|
||||||
|
# a GitHub visitor downloads, and it must not lag the sources
|
||||||
|
if os.path.isdir(os.path.join(book, ".git")):
|
||||||
|
blob = subprocess.run(["git", "-C", book, "show", "HEAD:main.pdf"],
|
||||||
|
capture_output=True)
|
||||||
|
tmp = os.path.join(book, ".committed-main.pdf.tmp")
|
||||||
|
with open(tmp, "wb") as fh:
|
||||||
|
fh.write(blob.stdout)
|
||||||
|
ctxt = subprocess.run(["pdftotext", tmp, "-"],
|
||||||
|
capture_output=True, text=True).stdout
|
||||||
|
os.unlink(tmp)
|
||||||
|
check(f"published {med.group(2)}" in ctxt,
|
||||||
|
"COMMITTED main.pdf carries the current-edition line "
|
||||||
|
"(the PDF a visitor downloads is not stale)",
|
||||||
|
med.group(2))
|
||||||
|
else:
|
||||||
|
ok("committed-PDF binding skipped (no git repo here)")
|
||||||
|
|
||||||
# ── Phase 2: internal countable claims ─────────────────────────────────
|
# ── Phase 2: internal countable claims ─────────────────────────────────
|
||||||
print("=== Phase 2: internal countable claims ===")
|
print("=== Phase 2: internal countable claims ===")
|
||||||
|
|
@ -151,9 +190,10 @@ def main():
|
||||||
# stale-total scan is scoped to the front matter, where whole-book totals
|
# stale-total scan is scoped to the front matter, where whole-book totals
|
||||||
# live; inside a chapter, "N chapters" is a positional count checked next
|
# live; inside a chapter, "N chapters" is a positional count checked next
|
||||||
for stale in ("twelve chapters", "thirteen chapters"):
|
for stale in ("twelve chapters", "thirteen chapters"):
|
||||||
hits = [f for f in ("main.tex", "README.md")
|
hits = [name for name, txt in (("main.tex", main_scan), ("README.md", readme))
|
||||||
if stale in read(os.path.join(book, f))]
|
if stale in txt]
|
||||||
check(not hits, f"no stale '{stale}' in front matter", ",".join(hits) or "clean")
|
check(not hits, f"no stale '{stale}' in front matter (history block exempt)",
|
||||||
|
",".join(hits) or "clean")
|
||||||
# any "spent N chapters" phrase inside chapter chNN counts its predecessors
|
# any "spent N chapters" phrase inside chapter chNN counts its predecessors
|
||||||
for cf in chapter_files:
|
for cf in chapter_files:
|
||||||
num = int(re.search(r"ch(\d\d)-", cf).group(1))
|
num = int(re.search(r"ch(\d\d)-", cf).group(1))
|
||||||
|
|
|
||||||
BIN
main.pdf
BIN
main.pdf
Binary file not shown.
33
main.tex
33
main.tex
|
|
@ -37,6 +37,39 @@ claim it makes about a proof, a proof assistant has checked.\par}
|
||||||
\restoregeometry
|
\restoregeometry
|
||||||
\pagecolor{paper}\color{ink}
|
\pagecolor{paper}\color{ink}
|
||||||
|
|
||||||
|
% BEGIN PUBHIST ==================== publication history =====================
|
||||||
|
% The current-edition line is machine-checked by check-book.sh: its chapter
|
||||||
|
% count and page count must match the built book, and the committed PDF must
|
||||||
|
% contain it. Historical lines are frozen and exempt from the claim checks.
|
||||||
|
\thispagestyle{empty}
|
||||||
|
\vspace*{2cm}
|
||||||
|
{\small
|
||||||
|
\noindent\textbf{Second edition} --- published August 8, 2026: fourteen
|
||||||
|
chapters, 129 pages.
|
||||||
|
|
||||||
|
\medskip
|
||||||
|
\noindent\emph{Publication history}
|
||||||
|
\begin{itemize}[leftmargin=1.4em]
|
||||||
|
\item \textbf{First edition}, July 3, 2026 --- twelve chapters and the
|
||||||
|
Interlude; expanded the same day with the pen-and-paper program and
|
||||||
|
in-book solution pathways (53 to 106 pages).
|
||||||
|
\item July 28, 2026 --- the Attestation Protocol added as a thirteenth
|
||||||
|
chapter.
|
||||||
|
\item \textbf{Second edition}, August 8, 2026 --- full didactic overhaul
|
||||||
|
(seven moves, from a seven-reader audit); new Chapter~13, \emph{The
|
||||||
|
Second Summit} (SLH-DSA, post-quantum); the Attestation Protocol becomes
|
||||||
|
the fourteen-chapter book's single finale; \code{check-book.sh} added ---
|
||||||
|
the script that verifies every countable claim in this book, including
|
||||||
|
the line at the top of this page, against measured reality.
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\medskip
|
||||||
|
\noindent The complete revision record is the git history of
|
||||||
|
\code{github.com/saymrwulf/verifying-crypto-with-lean}.
|
||||||
|
}
|
||||||
|
\clearpage
|
||||||
|
% END PUBHIST ================================================================
|
||||||
|
|
||||||
% ===================== HOW TO READ =====================
|
% ===================== HOW TO READ =====================
|
||||||
\chapter*{How to read this book}
|
\chapter*{How to read this book}
|
||||||
\markboth{How to read this book}{}
|
\markboth{How to read this book}{}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue