Implementation diary
Contents
Implementation diary#
Short, dated notes on design decisions taken during implementation — the raw material for this book part. Keep it as you go, not retroactively.
2026-07-26 — project start#
Branch created; design document and milestone plan written. Decisions D1–D4 (staged logic,
proof terms + independent checker over LCF, trace format designed up front, intuitionistic core
with a classical toggle) recorded in docs/PEANO_LAB_DESIGN.md §0.
2026-07-27 — M0 representation choices#
The pinned parser API returns only a de Bruijn tree, not the surface free-name table. Free names therefore receive indices in deterministic first-occurrence order; companion parsing helpers retain that table for the future UI. Bound variables still use ordinary de Bruijn depth. The canonical printer uses Unicode logical symbols and fresh deterministic binder names.
subst_termandsubst_formulamean binder-opening substitution: the selected slot is replaced, larger indices close the gap, and replacements shift under binders. An internal/publicshift_formulacompanion is necessary to express the quantifier and induction rules without capture. Tests include the classic free-variable-under-forallcounterexample.The certificate checker is bidirectional: introduction forms are checked against a target and elimination/equality forms synthesize their result. This preserves the pinned unannotated proof constructors and keeps the trusted code small; certificates are kept in checkable normal form.
Two architecture tensions are recorded rather than silently hidden.
EqSubstremains a kernel primitive because the API stub explicitly requires it, although design §1 also calls Leibniz substitution “derived”. The required IND certificate forforall x. x + 0 = xdeliberately exercises the schema but is logically redundant because PA3 is exactly that formula.Objection for M3 review: the binding design requires a classical DNE toggle, but the pinned kernel certificate language names only PA1–PA6 and has no DNE constructor. The tactic layer cannot soundly add DNE by itself. M3 will need an explicit, labeled kernel certificate form (and a checker mode or premise) before
classical oncan close any new theorem.An adversarial review found that Python subclasses could override an AST node’s equality and fool an
isinstance-based trusted recursion. The checker now admits only the exact frozen kernel constructor classes at every boundary. The concrete forged-Zero, forged-formula, and forged-proof attacks are permanent regression tests; this is a useful Python-specific extension of the De Bruijn criterion.
2026-07-27 — M2: induction is certificate construction#
induction nhas two honest readings. On an outerforall,nis a fresh surface name for that binder and the focused hole becomesInd(motive, base, step). Afterintro n, the name is instead a rigid context variable: the engine abstracts precisely that de Bruijn slot into the motive, builds the sameInd, and explicitly specializes the resulting universal certificate at the original variable. Neither path adds an induction oracle to the tactic layer.The step goal is displayed with a fresh
IHas its newest hypothesis. Its older hypotheses are shifted under the new natural-number binder, exactly matching the kernel context beneathForallIntro(ImpIntro(...)). The base goal retains the original context. Tests exercise both entry paths so a display-name shortcut cannot accidentally stand in for binder arithmetic.Universal
introlikewise shifts every hypothesis when it descends under a term binder.specialize h tderivesh[t]withForallElimand installs it through the same explicit local cut used by hypothesis rewriting; the original universal remains under a deterministic_beforename. Specialization requires a concrete term in M2—metavariable witnesses remain the explicitly scoped M3 existential feature.The first genuinely inductive ladder theorem,
forall n. 0 + n = n, now closes in six primitive tactics: induction, the PA3 base rewrite, reflexivity, the PA4 step rewrite, successor congruence, and the named induction hypothesis. The contrast with PA3’s immediaten + 0 = nis visible in the proof state rather than hidden in an evaluator.Audit added a small but important UI invariant: term-variable and hypothesis names share one visible namespace. Reserved parser words (
S,forall,exists,bot,false) cannot be binders, and generatedIH,_before, and_parameternames avoid both kinds of declaration. These collisions were not logical unsoundness—the kernel uses indices—but ambiguous or non-round-trippable proof states would violate the canonical-display law.
2026-07-27 — M3: resolving the classical boundary before coding it#
The M0 objection is real: the binding design simultaneously pinned an intuitionistic
check(ctx, proof, formula)API with no DNE certificate and required an OFF-by-default DNE toggle. An engine-only flag cannot authorize a certificate, while accepting an unlabelled or unconditional classical step would make “off” unenforceable at the trusted boundary.The smallest explicit amendment keeps
checkexactly intuitionistic and adds one inertDNE(proposition)certificate plus a siblingcheck_classicalentry point over the same structural recursion.checked_final(..., classical=False)receives that Boolean from the session owner, not from tactic-controlled proof state. Thus an injected DNE node is rejected in HA mode, accepted only in the labeled PA+DNE extension, and remains visible in the artifact. This is an implementation of design decision D4, not a silent change to the object logic.The v1 trace record has no mode field and its field set is already binding. Mode changes will be recorded as ordinary successful
classical on/classical offevents with unchanged goals; a replay reconstructs mode sequentially. Adding a field would require an explicit v2 format, so M3 does not mutate v1 under the table. The future session banner reads the same owner-held mode.M3 is also the first point where a witness metavariable can meet a later eigenvariable. A plain global
?twould allow the bogus routeexists ?; intro y; reflfor formulas such asexists x. forall y. x = y. Engine metas therefore carry a de Bruijn protection depth: lifting under a binder increments it, and unification may lower a candidate only when that candidate contains no locally bound variable. This keeps proof-wide inference useful without allowing witness escape or teaching a knowingly restricted fake version ofexists ?.Rewriting below a quantifier is sound for an equation already available outside that quantifier: at depth
dit matchesshift(source, d), insertsshift(replacement, d), and puts the motive placeholder atVar(d). A PA axiom cannot be naively instantiated with the target’s locally boundVar(0)outside the binder; users mustintro x; rewrite PA3unless a future dedicated binder-local transformer is added. Treating that local variable as outer would be capture, not convenience.
2026-07-27 — M3: connective certificates and adversarial closure#
applypeels leading universal binders into scoped metavariables and implication binders into ordinary premise goals; it never treats a successful unification as a proof. Every branch still installs an explicitForallElim/ImpElimcertificate. If a vacuous universal leaves an implicit term only in the certificate and in no open obligation, the engine chooses the canonical closed witness0. This is deterministic elaboration, not a logical rule, and the resulting certificate must still pass the kernel.casesmirrors each eliminator rather than destructively editing the context. Disjunction and existential elimination create branch/eigenvariable obligations directly; conjunction projections and rewritten hypotheses enter through explicit local cuts. Thus context names are UI conveniences while kernel hypothesis indices and eigenvariable conditions remain literal.<=/≤is parser and printer sugar only:a ≤ bis exactly∃k. k + a = b; there is no trustedLenode. The same canonical recognizer is used by rigid trace goals and theorem footers so training data cannot alternate between sugared and expanded spellings.The PA hint routine is deliberately observational. It has a caller-supplied check budget, allocates no holes or metavariables, and suggests only a primitive command that can be replayed. Failed mode changes likewise emit transactional error records; neither tracing nor hints can change proof authority.
Adversarial review exercised 5,625 generated scoped unifications, nested binder shifting, witness/case ordering, 30,000 rewrite cases, 1,000 complete rewrite certificates, 20,000 printer round trips, and 10,000 hint states. It found four contract gaps, all made permanent tests: proof-only vacuous instantiations needed deterministic grounding, truthy non-Booleans could impersonate classical authority, rigid
≤traces used the expanded spelling, and rejected mode commands were not logged. After repair the full Peano suite has 187 passing tests, the Lambda Lab regression suite remains 360 passing with 36 subtests, and the independent checker is 234 lines.
2026-07-27 — M4: automation must remain untrusted and replayable#
Tacticals will be ordinary functions from tactics to tactics, but a compound invocation is one user transaction: if any required branch fails, the caller still holds the exact input state; on success one
undorestores that input.thenandall_goalssnapshot the goals they mean to visit so a tactic cannot accidentally iterate forever over subgoals it just created.Closed computation has two deliberately separate products. A semantic evaluator may report that a closed equation is true or that a bounded quantifier search found no counterexample; only a proof-producing path may close a goal, and its output is still checked by the kernel. A bounded check is labeled with its finite range and is never promoted into a universal certificate.
Backtracking search will explore immutable states without logging speculative successes. Once it finds a complete plan, it replays only that winning primitive sequence through the normal tactic dispatcher and v1 logger. This keeps traces sequentially replayable while search remains free to be wrong; final QED still uses the externally owned original theorem and independent checker.
Objection for M7 review: the pinned proof language is bidirectional but has no formula ascription/cut node. The kernel can check an arbitrary closed proof of
∀x. φ, yetForallElimcan reuse it only when that proof also synthesizes its universal formula. M4simptherefore accepts PA constants, inferable checked rule certificates, and explicitly selected context hypotheses, but rejects a tagged closed certificate it cannot transport. Adding a small checked ascription node would be logically conservative, but doing so silently would violate the binding constructor list; revisit explicitly when M7’s theorem-library reuse makes the tradeoff concrete.
2026-07-27 — M4: termination orders, backtracking, and audit lessons#
Tree size cannot justify simp termination because PA6 turns
x · S yinto the largerx · y + x. The rule gate therefore uses lexicographic path ordering with· > + > S > 0; PA3/PA5 descend to a subterm, while PA4/PA6 decrease a recursive argument under a lower-precedence head. Pure permutations use a deterministic total extension and fire only in its decreasing direction. An optional step cap is labeled as a resource limit, never presented as the termination proof.A simp result is a normal formula plus an ordered list of equation proofs and motives. Closing a normal form uses only reflexivity, an exact hypothesis, or structural congruence; transport back nests explicit
EqSubstnodes. This separation made 500 randomized generated transports easy to submit directly to the independent checker.Search depth measures one proof-tree branch, so sibling goals reuse the same allowance. The first implementation nevertheless chose only the first solution of each sibling: if a later sibling contradicted a shared metavariable assignment, it never resumed the earlier choice. Turning child solving into a generator restored genuine backtracking. Complete leaves receive an advisory kernel check so a locally plausible but non-synthesizing certificate becomes another failed branch, not a misleading goals-closed result; external-original finalization remains the actual QED authority.
Focused tacticals exposed a second proof-wide metavariable lesson. A child run on one isolated goal made
_committhink an older shared meta was proof-only and default it to zero, hiding the sibling that would later infer one. Canonical grounding is now restricted to metas freshly introduced by the current tactic. Older metas remain flexible acrossfocus,then, andall_goals; the exact counterexample is a permanent kernel-checked regression.The plan’s “~100 lines” for tacticals proved incompatible with simultaneously making arbitrary goal focus certificate-hole-safe, validating child contracts, propagating substitutions, and collapsing compound history into one undo transaction. The file is intentionally about 270 comment-rich physical lines rather than hiding that machinery or deleting the pedagogical invariants. This is a deliberate clarity/soundness objection to the estimate, not a change to the tactical API.
Final adversarial passes covered 1,500 random search formulas (401 successful plans, all checked), 4,845 generated arithmetic decisions/certificates, 10,000 bounded formulas, certificate mutations, malformed states, nonzero focus under every binder/eliminator family, and huge search depths. The milestone closes at 277 Peano tests, 360 Lambda tests plus 36 subtests, and an unchanged 234-line trusted checker.
2026-07-27 — M6: prose that must execute#
A tactic encyclopedia is dangerous if its examples merely look convincing. The registry therefore has one card for every real primitive and tactical, and every example is a complete script replayed in a fresh
LabSessionthrough checked QED. Goal effect and certificate effect are separate fields:simp, for example, may finish a normal equality with explicitCongS/CongAdd/CongMul, not onlyReflor a hypothesis. An independent content audit caught and corrected that distinction.KB cards are immutable UI data with no route into the kernel. They state the six rule constants exactly, distinguish de Bruijn indices from the De Bruijn criterion, and say what checking cannot establish: bounded-search failure is no verdict, derivability is not standard-model truth, and a small checker is not a proof of its own correctness or of PA’s consistency.
The tutorial state machine owns raw lines, but its frozen proof commands must not be sent back through that same owner. Each chapter therefore keeps a private nested proof-session dictionary and calls the production
ui.provepath directly. ENTER advances only after a command succeeds; failed commands remain on the exact step, and a QED-gated chapter cannot complete until the nested session has closed throughchecked_final.The first draft of the
add_commlesson proved an implication from two earlier ladder rungs. That was honest but did not meet “prove add_comm by hand.” Replayingauto’s winning trace exposed a clearer premise-free nested-induction script; the tutorial now executes those primitive/simp steps without callingautoand checks the actual theorem∀n m. n + m = m + nfrom an empty context.The existing book gate had two top-level modules both named
driver. They are loaded under distinct aliases, then links route by/peano-lab/path orpacommand and fenced blocks route by theirλ>/pa>prompt. Failure detection is line-oriented so explanatory cards may discuss errors while actual tactic errors and rejected QEDs still fail the build. Both source-fallback and built-HTML extraction are tested. The page BUILD tag moved to2026-07-27bso cached M5 workers cannot omit the four new UI modules.Browser drivers deliberately catch unexpected Python exceptions and print a one-line class name, so searching only for a traceback let a crashed command pass the first dual-driver gate. The gate now recognizes line-anchored
*Error:/*Exception:results for both labs while a card may still discussValueError:inside a sentence. The exact staged 25-file worker payload was finally loaded under pinned Pyodide; it rendered both registries and completed the ENTER-onlyadd_commtutorial through checked QED. M6 closes at 373 Peano tests, 360 Lambda tests plus 36 subtests, a warning-free 17-page Jupyter Book build, and an unchanged 234-line trusted checker.
2026-07-27 — M7: theorem reuse without a trusted theorem oracle#
The M4 objection about reusable introduction-form certificates is real: the binding kernel can check a
ForallIntroproof at a known formula but cannot always synthesize that formula when the same proof is placed underForallElim. I am not adding an ascription constructor or a trusted theorem environment. Each library script instead proves a curried goal whose earlier rungs are ordinary local hypotheses. The untrusted library layer then performs explicit proof-term cut elimination, replacing those hypothesis references with the earlier closed certificates and adjusting de Bruijn hypothesis indices beneath implication, disjunction, and existential binders. The independent kernel finally checks the composed certificate against the original, dependency-free theorem statement. A composition bug can therefore only be rejected.Library entries remain replayable data: one closed statement, an acyclic list of earlier rungs, and a sequence of primitive tactic commands. Generated dependency introductions are displayed separately from the authored body so the browser can explain both what theorem was claimed and how theorem reuse was compiled away. This keeps the kernel fixed at the binding rule set while making the proof dependency graph visible to students.
The first cut eliminator passed arithmetic but failed at the two-dependency antisymmetry helper. Two distinct scope hazards were exposed. Sequential dependency passes could revisit hypotheses internal to an already inserted certificate, so dependency slots are now substituted simultaneously. The substitution also exposes
ForallElim(ForallIntro(...), t)andImpElim(ImpIntro(...), p)redexes; both must be contracted capture-safely because the bidirectional checker rightly refuses to synthesize arbitrary introduction forms. The complete helper chain now checks from the empty context, and a permanent regression targets the original multi-dependency failure.A named
mul_succ_lefthelper is intentionally included even though it is not one of the binding headline rungs. It turns multiplication commutativity from a 21-command, 266-node nested proof into a six-command proof whose algebraic idea is visible. The same rule applies to the order helpers: naming the genuine mathematical obstruction is clearer than hiding it inside a giant tactic script, and every helper remains an ordinary kernel-checked theorem.M7 closes with all twenty entries checked from the empty context, 1,300 certificate nodes across the library, and all twenty generated Lean statements accepted by Lean 4.28 (the only warning is the deliberately visible proof stub). Three audits mutated every certificate, shifted goals, attacked proposition/term capture, and checked the browser/Lean presentation. The final suites report 433 Peano tests and 360 Lambda tests plus 36 subtests; the trusted checker remains 234 lines. The exact staged 28-file payload also replayed the whole ladder in pinned Pyodide.
2026-07-27 — M8: turn the implementation record into a course#
The binding outline becomes six narrative chapters rather than one long retrospective: motivation and staging; the kernel boundary; a tactic’s anatomy; tacticals as a language; induction and the ladder; and deliberate limits. The executable tutorials and theorem reference stay as companion pages. This keeps the main argument readable while preserving the exact commands and full scripts where students need them.
The chapters are built from this diary, but diary claims are not treated as evidence. Commands and browser links use the production grammar and pass through the dual-driver book gate; source claims link to the implementation or tests that enforce them. A polished explanation may compress an incident, but it must not invent a proof or silently improve the running system.
The landing page now says “live” and makes the trust story the announcement: tactics construct, the independent 234-line kernel checks. It links both the browser surface and this construction account, and names the twenty-entry checked ladder rather than promising an unspecified future prover.
Three independent prose audits compared the chapters line by line with the implementation. They caught eight small but meaningful overstatements:
autopreserves primitive undo entries, a trace row’s rendered goals are not a full replay snapshot, permutativesimprules are ordered at each instance, traced programmatic calls require a logger, and the capstone base proof ignores its PA5 premise. After correction, the six chapters contain 15 live links and 45 replayed commands.M8 closes with a warning-as-error 24-page book build and a full gate over 190 links plus 78 session commands. The vault has 49 notes with no unresolved wiki-links; the suites report 436 Peano tests and 360 Lambda tests plus 36 subtests; the trusted checker remains 234 lines. The in-app browser runtime is still unavailable, so the visible landing panel remains an explicit manual DOM check.
2026-07-27 — M9: data without a second meaning of proof#
The logger’s binding v1 rows remain immutable. Exported
train.jsonlandval.jsonlcontain the same nine ordered transition fields; theorem text and QED metadata belong to footers and aggregate statistics, not an invented v2 training row. Deduplication ignores only session identity and step number, while keeping every field that changes the learning problem—including failure text.Input continuity and output use are different contracts. The exporter must validate each complete contiguous session, sequential steps, transactional errors, and exactly one adjacent footer. After validation, train/validation files are independent transition examples, so removing duplicate rows is allowed to leave gaps in their original step numbers. The theorem/session group is nevertheless the split unit, and a semantic duplicate may never leak into both sides.
Synthetic generation is not permission to fabricate JSON. Every row must come from the production
TraceLoggerwhile the real engine attempts a real tactic. Successful sessions finish only throughchecked_finalagainst their owner-held original goal; honest failed ladder sweeps and deliberately inapplicable tactics remain useful negative records.The evaluator follows the same rule. A policy may stop, exhaust its budget, or produce an apparently closed state; an attempt counts only when the independent checker accepts the final certificate for the original theorem. A deterministic random policy is therefore a plumbing baseline for the whole proposal-to-kernel path, not a miniature theorem-proving result.
A seed is not a run identity. Two batches can share a seed while differing in configuration, theorem fixtures, source, checker, or Python runtime. The generator now hashes all of those inputs into a run fingerprint and prefixes every session ID with it, so honest multi-file collation does not turn distinct traces into apparent duplicate sessions.
The exporter must bind metadata back to state, not merely validate its shape. A footer claiming a different theorem could evade exclusions and poison theorem-group splits even though every JSON field looked valid. The strict importer therefore requires exactly one initial goal and equality between its canonical post-turnstile target and the footer theorem. It also recognizes case-insensitive/hard-link input aliases and publishes all three output files with rollback.
“The policy does not see the theorem name” includes indirect channels. The first evaluator seeded its policy-visible RNG from that hidden name, and it recreated metavariable aliases each turn; both made evaluation differ from the promised trace input. Seeds now derive from the visible canonical goal, aliases live for the full rollout, and the four literal held-out statements carry a fixed SHA-256 checked against the theorem library.
Error categories are control flow, not prose. Ordinary
TacticErrorremains recoverable inside tacticals, malformed surface input is a non-recoverableTacticSyntaxError, and resource exhaustion isTacticLimit:repeatpropagates it whilefirst/orelseretain it when no alternative succeeds. That distinction now survives boundedauto, boundedsimp, planning, and replay, so hostile tactic arguments cannot spoof evaluation status through English substrings. The final audit also reproduced absent case-only and Unicode-normalization output aliases on this Mac, plus an ancestor/child output topology; all are rejected before either artifact path is created.M9 closes with a 13,152-row committed release (12,540 train / 612 validation), produced by 1,596 independently finalized synthetic sessions with all ladder sources disabled. The separate all-ladder smoke generated 13,417 raw and 13,412 unique exported transitions, including 20 honest bounded-auto attempts and 20 checked authored replays. The random baseline ran 32 pinned held-out attempts through the production grammar and kernel judge; its 0.0 pass@8 is an honest plumbing result, not a theorem-proving claim. Focused M9 reports 62 tests, the full Peano suite 498, Lambda 360 plus 36 subtests, the book/gate and 52-note vault are clean, and the checker remains 234 lines.
2026-07-27 — M10: a theorem environment compiled away#
The odd-square induction exercise exposed a precise usability gap: the witness and induction hypothesis were correct, but live proofs could browse checked associativity, commutativity, and distributivity without being able to use them. Re-running those lemmas by nested induction inside every theorem would be sound but pedagogically perverse.
use <library-theorem> [as <alias>]is intentionally a surface bridge, not a new kernel proof source. The UI resolves a replayed theorem; the engine rechecks the closed formula/certificate pair and insertsImpElim(ImpIntro(hole), certificate). Existing tactics then see an ordinary hypothesis.Tactical history cannot identify imports:
use add_comm; exact add_commbecomes one outerthentransaction. Surface finalization therefore examines a completed certificate, contracts its exposed implication/forall cuts in a transient state, and callschecked_finalagain with the owner’s original target and logic mode. The raw immutable state remains available to exact undo.A final resource audit found that distinct aliases could otherwise grow this temporary cut tree until Python recursion failed outside the tactic error path.
usenow measures theorem and live certificates iteratively, applies explicit node/depth limits transactionally, and QED maps any remaining host recursion exhaustion to anInvalidProofwhile retaining the session.usesolves availability, not symbolic polynomial normalization. A two-lemma additive example now closes in milliseconds, while bounded trials of the odd-square step still makesimpexpand impractically. That evidence fixes the next boundary: M11 supplies a checked semiring basis and M12 builds a certificate-producingring; the kernel remains unchanged.M10 closes with the two-import theorem independently checked, 520 Peano tests and all 360 Lambda tests plus 36 subtests green, 190 links/18 blocks/85 commands replayed, the warning-as-error book green, and 53 vault notes/238 links/0 unresolved. The source-bound 13,152-row corpus was regenerated from 1,596 checked sessions; the trusted checker is still exactly 234 lines.
2026-07-27 — M11: only the missing algebraic orientations#
The semiring audit began from the normalizer’s proof obligations rather than a wish list of familiar lemma names. PA3 and PA5 already give the right zero laws;
zero_add,mul_zero_left, both commutativity and associativity laws, andmul_addwere already checked rungs. Addingadd_zero,mul_zero, successor-as-plus-one, or numeral-specific laws would duplicate that base.Exactly three entries were missing:
one_mul,mul_one, and left distributivityadd_mul. Their authored scripts use the existing induction and simplifier surface; dependencies remain earlier and acyclic. Their final certificates have 26, 31, and 748 nodes and all check from the empty context. The largest has depth 45, well below M10’s 128-level import limit.Capture tests import each certificate below both a universal binder and an implication binder, then specialize it with terms containing the outer variable (
x,S x, andx + 1). QED cut compilation and the independent checker accept the exact wrapped statements. This tests the representation boundary that M12 will rely on, not merely three top-level equations.M11 closes with 84 focused and 527 full Peano tests green, all 360 Lambda tests plus 36 subtests, the three Lean 4.28 stubs elaborated, and all 190 links/18 blocks/85 commands replayed. The warning-as-error book and 54-note/247-link vault are clean; the regenerated 13,152-row corpus records all 23 rungs; the checker remains exactly 234 lines.
2026-07-27 — M12: computation chooses, certificates justify#
The odd-square exercise fixed the user-facing boundary. The witness is
x + S n, but askingsimpto discover and replay the polynomial rearrangement is both opaque and impractical.ringinstead has one narrow job: close a focused equality when its two sparse commutative-semiring normal forms are identical.The sparse calculation is not trusted. Successor is certified as addition by one from PA3/PA4; identities, AC permutations, and distribution use the closed M11 law certificates; coefficient arithmetic produces PA3–PA6 proof terms. Supplied laws, instantiated laws, and the finished certificate are checked before the state is published, and ordinary QED checks the original theorem once more.
ringtakes no arguments and never mines the local context. The readable induction step usestrans ((2*n+1)*(2*n+1)) + 8*S n, proves that identity withring, rewrites forward byIH_witness, and callsringagain. This explicit proof structure was preferable to a hypothesis-aware mini-solver hidden behindring [IH_witness].Browser predictability is part of the contract: 256 AST nodes/depth 64, 16 variables, degree 16, 64 monomials, coefficient 128, 25,000 work units, 100,000 proof nodes/depth 256, and a five-second wall-clock budget. The required large normalization measured about 1.4 seconds under native CPython; the in-app browser was unavailable, so a direct Pyodide timing remains a deployment check. Metavariables and non-equality goals are rejected; differing normal forms are a transactional
TacticError, while exhausted budgets are a transactionalTacticLimit.The integrated gates are green. The exact odd-square transcript reaches checked QED; mutations of its coefficient, constant, witness, middle expression, proof leaves, or context discipline are rejected transactionally, as are forged basis laws and every explicit resource limit. Peano has 581 passing tests; Lambda has 360 tests plus 36 subtests; the warning-as-error 24-page book replays 190 links/19 blocks/96 commands; the vault has 55 notes/258 links/0 unresolved. The regenerated source-bound corpus keeps 13,152 rows from 1,596 checked sessions, local staging is green, and the trusted checker is still 234 lines.
2026-07-27 — M13: calculate narrowly, justify completely#
norm_numdeliberately computes less than Python could. It sees only closed numerical islands in an equality (optionally below a bounded leading universal spine), chooses a unary numeral, and builds the PA3–PA6 and congruence proof that the kernel expects. A computed Boolean or integer is never proof authority; the bridge is checked before commit and the original theorem is checked at QED.Open normalization remains honest. If the two normalized terms are not identical, the tactic transports one explicit residual goal back to the original equality. It does not read hypotheses as rewrite rules. This keeps the teaching distinction sharp:
simprewrites,norm_numcertifies concrete arithmetic,ringcertifies unconditional polynomial identities, andautosearches. General PA and nonlinear consequences of assumptions are outside all four; a futureomegarequires its own certificate language and limits.Browser safety is part of the semantics exposed to students: term shape, leading binders, computations, values, work, generated bridge, complete live proof, and wall time are all bounded. Every ordinary failure or exhausted limit is transactional. The pure hint path uses the same focused hole and projected immutable commit, but consumes no hole ID and publishes no history.
The teaching surface now includes a tactic card, checked tutorial, this executable chapter, a connected vault concept, controlled failure/success traces, and a generator-v2 numerical corpus tranche. The reproduced v1 release has 13,344 unique rows from 1,692 checked QED sessions; its 18-row validation split is explicitly only a same-family pipeline check.
M13 closes locally with 641 Peano tests, 360 Lambda tests plus 36 subtests, a warning-free 25-page book whose 193 links and 125 commands replay, 56 vault notes/271 links/0 unresolved, green corpus smoke and evaluator-v2 plumbing, green staging/vendor hashes, and the unchanged 234-line kernel. No in-app browser instance was available, so direct Pyodide interaction is left as an explicit publication limitation rather than an invented measurement.