The durable design record and provenance for this proof. It captures why the proof is built the way it is: the goal, the artifacts it relates to, every load-bearing design decision and its rationale, the proof strategy and architecture, the faithfulness ("does this really mirror the paper?") evidence, and the deliberate scope boundaries.
The proof is complete (the eleven phino normalization rules are confluent on
well-formed terms; #print axioms PhiConfluence.confluence = [propext, Quot.sound]).
This document is therefore a finished-project record, not a roadmap. Two companions own
the other halves: the exact theorem statement, the rule⇄constructor table, and the eight
deviations live in M0-spec.md (the frozen contract); the module layout and the
"what to run" quickstart live in README.md.
A fresh, machine-checked proof in Lean 4 that the normalization (reduction) rules of the
current φ-calculus are confluent (Church–Rosser) — the order in which rules fire never
changes the final result. The proof is about the calculus as implemented by phino
(the reference manipulator), which is the same calculus the paper renders.
It supersedes the earlier minimal/extended development in this repository's history, which proved confluence only for the minimal calculus and never finished the EO-matching variant.
| Artifact | Repo / location | Role |
|---|---|---|
| The paper | objectionary/calculus-paper |
The informal spec. Its Fig. 4 rules and Appendix-A example reductions are auto-generated by phino (\iexec{phino explain --normalize}, phino rewrite), checked in its CI. |
phino |
objectionary/phino |
The authoritative, executable rule spec. Rules live in resources/*.yaml. |
| Earlier proof | this repo's git history | The retired Minimal/Extended development; reused only as a technique template (Takahashi parallel-reduction skeleton, Record design). |
| This project | the present repo (objectionary/proof) |
The new Lean 4 proof. |
Key structural fact: because the paper's rules and examples are generated from
phino, "match the paper" reduces to "match phino", which is executable and testable.
(Two phino bugs surfaced and were fixed during this work: the alpha-ordinal asset-counting,
phino #749, and a duplicate-ρ printer bug, phino #748 — our model already matched the
paper-faithful side of both. See M0-spec.md.)
The reduction relation ⟶ is the compatible (congruence) closure of the eleven
phino rules: dot, copy, alpha, phi, stay, over, stop, null, miss, dd, dc, over Term
(formations ⟦B⟧, applications e(τ↦e'), dispatches e.τ, the locators Φ/ξ, and the
terminator ⊥). The full pattern→result table, side conditions, and the eight deviations
from a naive "paper Fig. 4 verbatim" reading are in M0-spec.md (not duplicated here).
Two facts from that table shape everything below:
- The system is non-terminating (
⟦x↦y,y↦x⟧.xdiverges), so Newman's lemma is unavailable — confluence must come from the diamond, which needs no termination. dot/copycarrynfguards (andcopyaξ-freeness guard), so each rule is single-path. This removes theRcopy↔Rdotordering ambiguity that blocked the old proof, at the cost of making⟶a conditional relation with non-monotone guards — the central proof subtlety.
| Decision | Rationale |
|---|---|
| Source of truth = phino + calculus-paper LaTeX source (never the arXiv PDF) | The PDF is a stale build; the repo source regenerates rules from current phino. Paper-first, phino as its executable interpretation. |
Prove via parallel reduction → diamond → Relation.church_rosser |
The system is non-terminating, so Newman's lemma is unavailable. The Tait–Martin-Löf/Takahashi method needs no termination. |
Build on mathlib's Prop-valued Relation API |
Idiomatic, least code; church_rosser already proves "diamond ⇒ confluent-closure". |
Named attributes (φ, ρ, αᵢ, label) |
Matches the paper and phino; makes contextualization C and the αᵢ ordinals natural. (De Bruijn would obscure named-attribute semantics.) |
nf defined structurally (like phino's isNF), not as "no Step" |
Avoids an import cycle and the big-step circularity; a local property, well-defined regardless of confluence. A single nf encodes every rule's redex, including copy's ξ-free guard in its void-application case. |
Executable reduceStep separate from relational Step, linked by reduce_sound |
Lets the demo run and print traces, while a proof certifies the printed steps are genuine Steps — a tighter link than phino has (its Haskell engine is not proven against its YAML). |
copy is ξ-free and local (no scope/contextualize) |
Under ξ-freeness, contextualize(e₁, ctx) = e₁ is proved (contextualize_eq_self, Parallel.lean, [propext]), so the paper figure's C(e₁⊳scope) equals our slot value e₁ for any scope; dropping scope/contextualize is exact, not a weakening, and keeps copy inside the de-risked fragment. (M0-spec dev. #7; the author is correcting the paper + phino to match.) |
Headline is WF-scoped |
Two clauses, different weight. legalKey (no αᵢ formation key) is necessary for confluence: the counterexample that makes unconditional Confluent Step false once alpha is present is a legalKey violation (a malformed αᵢ-keyed void slot lets alpha/over fork to never-joining object/⊥), and the diamond proof consumes exactly this clause. Nodup (unique keys) is faithfulness, not a shown necessity — no duplicate-key non-joinable fork is known and the diamond binds-but-never-uses it; it matches Def. Binding and makes first-match lookup agree with phino's matcher. Both re-impose the paper's own grammar. (M0-spec, "Why WF-scoped"; dev. #6/#8.) |
- Define single-step
Step(⟶): the eleven rules + the four congruences, phino-faithful. - Define parallel reduction
Par(contracts any set of redexes at once; congruence built in) and a total complete developmentdevel. - Prove
Step ⊆ Par ⊆ Step∗, soReflTransGen Step = ReflTransGen Par(redMany_eq). - Prove the Takahashi triangle
WF e → Par e u → Par u (devel e), giving the diamond. Abstract.Diamond.confluent(viachurch_rosser) turns the diamond into confluence; transport toStep(the closures coincide).- Define
≡as convertibility; confluence makes it anEquivalenceon{e // WF e}.
The hardest parts, all discharged: the "C commutes with reduction" lemma
(par_contextualize_ctx), the non-monotone nf guards threaded through parallel
reduction, and the ρ-feedback of dot (the source of non-termination).
Three structural decisions carry the proof; each load-bearing shape was validated
[propext]-clean against Lean v4.30.0 + mathlib.
-
Well-formedness as a
Proppredicate. MutualWF/WFB(WellFormed.lean, importing onlySyntax) carried as a hypothesis on the diamond/headline — not an indexedBindingtype (which would force re-derivingSyntax/Step/Attributes). It encodes dev. #6 ((domain bs).Nodup) and dev. #8 (no positionalαᵢkey). Its preservation engine (domain_append/domain_set, mirroringlookup_set_*) rests on a single fact: reducing a binding's value never changes thedomain, soWFsurvives reduction (WF.step/WF.par,Preservation.lean). -
Parallel reduction
Par= mutualPar/ParBwith a bespoke cons-structuredParB(theList.Forall₂ ParBindroute is kernel-rejected as a nested inductive carryingPar). The append-index lives only onStep.congForm, crossed once byparB_set. Mutual induction goes throughinduction h using Par.rec (motive_2 := …)withmotive_1inferred. The cons-liftredMany_form_consis proved byinduction … generalizing+form_step_inv(notReflTransGen.lift— unsound, becausestayturns app→form). -
The Takahashi guard-on-developed-subterm variant (this, not Huet single-step on
Step). Withξ-freeness dropped fromcopy,dot's result places the developed formation⟦bs'⟧in both thecontextualizereceiver and theρ-argument, so reduction is duplicating and adot-vs-sibling fork needs >1 step on both sides, breaking Huet. OneParstep contracts all copies at once, so the triangle needs no non-duplication argument. The parallel-step constructors checknfon the developed childe₁', not the originale₁(e.g.Par.dotreads the guard offdevelB bsvialookup, keepingdevelstructural) — this restores a totaldeveland the triangle.
WF-relativization (the bridge). church_rosser / Abstract.Diamond.confluent demand
an unconditional strip ∀ a b c, r a b → r a c → ∃ d, …. A WF-hypothesised strip does
not discharge it for the full calculus (off-WF, the alpha-vs-over diamond genuinely
fails). The fix: relativize to ParWF a b := WF a ∧ Par a b, prove WF.par (so ParWF
stays WF-rooted), whence the strip for ParWF holds unconditionally (it is vacuous
when ¬WF a); feed THAT to the generic diamond to get Confluent (ReflTransGen ParWF),
then bridge back to WF-rooted ReflTransGen Step via redMany_eq. Diamond ParWF needs
WF of the source only, so no new Abstract lemma is required (Diamond.lean).
Lean's kernel guarantees soundness (the proof establishes the statement). It does not guarantee adequacy (the statement/definitions capture φ-calculus) — that gap is irreducible when one side is an informal paper. It is shrunk and cross-checked from several independent directions:
- Tiny, human-readable trusted surface. Only
Syntax,Step,↝∗, and theconfluencestatement must be read and endorsed (a few dozen lines — the M0 spec). - Rule transcription vs phino. The eleven-rule display table (
Rules.lean) is generated from phino'sresources/*.yamlbygen-rules.pyand pinned by therules-in-syncCI job, so the displayed rules cannot drift from phino/the paper. The proof relationStepis hand-written (constructors are needed for case-analysis) and pinned to phino behaviorally bydifftest. - Differential testing against phino.
Difftest.lean+scripts/difftest.shnormalize each program with bothphino rewrite --normalizeand our reducer and assert equality — 20/20, exercising all eleven rules (incl.alpha's domain-ordinal skip ofΔ/λassets) on⊥-collapse and real formation results. This is the samephino rewritemechanism the paper's Appendix A is generated from, so it doubles as reproducing the paper's examples (the fragment-fit subset; the rest needλ/Δdataization — see §10). #print axiomsCI gate. The headline results (confluence,conv_equivalence,reduce_sound,par_triangle,parWF_diamond,nf_iff) are gated to depend only onpropext/Quot.sound— neversorryAx,Classical.choice, ornative_decide.
Before investing in the Lean diamond, the central open question — does the full eleven-rule
calculus even have the diamond, given the non-monotone nf guards? — was stressed both
empirically and by structural analysis. The empirical probe (scripts/confluence-probe.sh) is a
fixed corpus of 7 hand-crafted programs plus the paper's Appendix-A examples, each run under
rule-order --shuffle and an off-strategy single-rule redex-position check; no divergence was
found. (This is a fixed corpus, not random fuzzing — there is no term generator.) The analysis
concluded confluence holds, for reasons that the mechanized proof then made rigorous:
- No genuine root critical pairs. The two LHS-overlap groups are mutually exclusive by
guards along root-stable axes — dispatch
⟦B⟧.τ{dot, null, phi, stop, dd} by slot-state (attached/void/absent) ×φ-membership × subject-head; formation-app⟦B⟧(τ↦e){copy, alpha, over, stay, miss, dc} by slot-state × name-kind (positionalαᵢwithindex = |B₁|→ alpha vs named → copy vsρ→ stay) × subject-head. This rests on three facts: duplicate keys are barred (a slot is in exactly one state),α-names cannot be formation slots, andindex()is defined only forαᵢ— exactly theWFinvariants. - All non-root critical pairs join. The duplication diamond (
dotrelocates and contextualizes a redex-bearing sibling into theρ-context — reduce before/after → identical NF) and the discard diamond (over/null/stop/miss/dc/dd erase to the absorbing⊥regardless of inner activity — which is exactly why they need nonfguard, and why Church–Rosser holds despite non-termination). - The non-monotone guard is real but benign. Reducing inside
e₁can expose a fresh outerdot/copyredex, so the naive maximal development and one-step triangle break. But the non-monotonicity serializes (no competing root redex exists while¬nf(e₁)) and is monotone in the destruction direction (no present guarded redex is destroyed by a sibling contraction) — which is precisely what the guard-on-developed-subterm variant (§6) exploits.
Routes considered and rejected: Huet single-step strong confluence (broken by dot's
duplication once ξ-freeness is dropped), orthogonality (non-left-linear LHS repeat τ),
Hindley–Rosen (no mathlib commutation API), decreasing diagrams (no Lean port, unneeded).
paper ──(phino explain/rewrite, calculus-paper CI)──▶ phino rules + example reductions
▲ │
│ (CI diff, this project)
│ ▼
└────────────────────────────────── our printed rules + our reductions (the demo)
│
(Lean: reduce_sound, reduceStep ⊆ Step)
▼
relation Step
│
(Lean kernel: no sorry/axiom)
▼
confluence theorem
Exactly: (a) Lean's kernel; (b) the definitions + the theorem statement; (c) for the demo/CI, the term printer/parser bridge and phino. The proof adds nothing to (a)–(c).
Demo (lake exe demo): prints the normalization rules in the paper's Unicode notation
and reduction traces of example φ-programs computed by our own reduceStep. reduce_sound
proves every printed step is a genuine Step, so the runnable behavior is tied to the
relation the confluence theorem governs.
Single-source rules. The displayed Rules.lean is generated by scripts/gen-rules.py
from phino's resources/*.yaml — the same source the paper's Fig. 4 is rendered from — so
the displayed rules cannot drift from phino/the paper by construction.
CI (single-purpose workflows, each on push to master + PRs, tracking phino-latest):
- build — install elan,
lake exe cache get,lake build; then asorry/admit/axiomsource gate and a#print axiomsgate on the headline results. - rules-in-sync —
scripts/regen-rules.shclonesobjectionary/phinoand regeneratesRules.lean;git diff --exit-codefails if our committed table has drifted from phino. - difftest — install the
phino-latestbinary, builddifftest, runscripts/difftest.sh; fails if our reducer disagrees with phino.
The two Lean workflows share a composite action (.github/actions/setup-lean) that caches
~/.elan and .lake so Lean/mathlib aren't re-downloaded each run.
The paper (foundations.tex, Def. Parent) and phino treat every formation as carrying a parent
ρ, void until set (a this-pointer, void until a method is called). The paper grammar
(syntax.tex) does not mandate ρ — a formation may be written ⟦⟧ — so the parent is supplied
semantically: phino materialises a ρ↦∅ appended at the end of every formation that lacks one
(verified: ⟦x↦Φ⟧ ⟶ ⟦x↦Φ, ρ↦∅⟧, recursively; explicit ρ is kept in place, never duplicated).
Omitting it would diverge from phino and the paper three ways: rule choice (⟦⟧(ρ↦Φ) is
copy→⟦ρ↦Φ⟧ for phino but miss→⊥ without ρ), alpha indexing (its positional index counts the
trailing ρ: ⟦x↦∅⟧(~1↦Φ) ⟶ ⟦x↦∅, ρ↦Φ⟧), and normal-form shape (every formation NF carries ρ↦∅).
We model it as a canonicalisation (Canonical.lean), faithfully:
canon/canonB— the injection: recursively appendρ↦∅to everyρ-less formation (explicitρkept in place), exactly phino's behaviour.Canonical/CanonicalB— the invariant "every formation carries aρ".canon_canonical([propext]) —canonalways establishes it;wf_canon([propext, Quot.sound]) —canonpreservesWF(it addsρonly when absent → no duplicate key, andρis legal);step_canonical([propext]) — reduction preservesCanonical, i.e. phino's parent-everywhere term space is closed under ourStep.
Because canonical terms are WF, the headline confluence : WF e → … already governs them — so the
result is confluence of phino's actual calculus, not a ρ-free fragment. With alpha now able to
rename onto a void ρ (then copy fills it), difftest matches phino on real formation results,
including the cases that diverged before canon (⟦⟧(ρ↦Φ), ⟦x↦∅⟧(α1↦Φ), bare value formations).
This was not a threat to confluence (the implicit ρ is a representational default, not a rule;
routing ρ-application to copy removes the miss-on-ρ case and adds no critical pair) — it was a
term-level fidelity obligation, now discharged both formally (the three lemmas) and behaviourally
(difftest 20/20).
WF-scoped, by design. The headline carriesWF e=legalKey∧Nodup. OnlylegalKey(noαᵢformation key) is necessary for confluence (thealpha-vs-overcounterexample is alegalKeyviolation; the diamond proof uses exactly this clause).Nodup(unique keys) is a faithfulness clause — matches Def. Binding, makes first-matchlookupagree with phino's matcher — carried but unused by the diamond argument (§4, M0-spec "WhyWF-scoped"). (phino once emitted a non-Nodupterm —⟦ρ↦⟦⟧⟧ ⟶ ⟦ρ↦⟦ρ↦∅⟧, ρ↦∅⟧(duplicateρ) — a printer bug contradicting its own Def. Binding; fixed in phino #748. Our model already gave the WF answer⟦ρ↦⟦ρ↦∅⟧⟧, so we now agree.)λ/Δatoms are outside the normalization relation⟶. They have no rule among the eleven. Their reduction is the paper's separate Morphing (fig:morphing—Mlambdacalls a host atom by value) and Dataization (fig:dataization) partial functions: stateful, side-effecting, host-dependent — functions, not term rewriting (so the relevant property there is determinism, not confluence). Confluence of⟶neither needs nor includes them;λ/Δare represented as inert atoms (Binding.lambda/Binding.delta) that never fire. This is the reason the paper's Appendix-A examples are validated throughdifftest(against a mergedruntime.phi) rather than re-encoded in Lean.- Small-step
copy, not the old big-stepRcopy. The current paper'scopyis the small-stepnf-guarded form we prove; the superseded big-step form (arXiv v9, which presupposed unique normal forms = confluence) is not modelled. - Encoding looseness (low, intentional).
Step.alpha/Step.copycarry no per-ruleNodup/legalKeypremise at the constructor, relying on the headline'sWFto exclude malformed redexes globally (matching phino, which checks no such premise). LikewiseWF'sNodupis overdomain(assets excluded), so it does not forbid duplicateλ/Δassets — fine while assets are inert, a latent looseness to tighten only if asset reduction is modelled.
Mechanically generating the Lean Step relation (not just the display table) from phino's
YAML, so the proof object and the paper's figure share a single source. Today Step is
hand-written and pinned to phino behaviorally by difftest; this would make the pin
structural. Optional — it does not affect the proof's validity.