Stage-5 LR design map — the user-effect binary LR (#44 Stage 5)
Verdict (one sentence). Stage 5 is ONE grind session, not more: the ADR-0092 §D3-as-landed ret-shape clause restriction makes the custom resume focus definitionally a
ret-of-closed-value (build-checked,custom_resume_is_ret), so the three debts are mechanical transcriptions of the already-proven state/txn arms — the hard continuation-capture case D3 was designed to exclude does not arise in v1. The riskiest arm iskrelS_custom_reinstall(debt 2), whose only genuinely new sub-proof is aHandlerRel-custom relation + a substituted-clause-valueVrelKhelper; the rest is a copy ofkrelS_state_reinstall, and it is strictly simpler (read-only param ⟹ the reinstall diagonal isp=p, where state'sputreinstalls a changed value).
Probe branch probe-stage5-lr, witness scratch/Stage5LRProbe.lean (all three statements
compile with sorry against main; custom_resume_is_ret/ClauseRel fully elaborate).
Census: the probe is scratch (not imported by Bang/Audit.lean) → feeds NOTHING in
just axioms. The three real debts are the four already-flagged lr_* stubs
(crelK_fund/krelS_refl custom arms, BinaryLR.lean; dispatchOn_rename custom arm,
LR.lean) — all inside the flagged-7 lr_sound/lr_fundamental set; Stage 5 adds NO new
flagged headline and, when done, un-flags nothing new (it closes the custom slice within
the still-deferred lr_sound cluster — see the PATH W1/W2 walls, orthogonal to these debts).
The ret-shape tractability answer (the probe's most valuable output)
ADR-0092 §D3-as-landed pins each v1 clause body to Comp.ret w (HasClauses.cons, with
w : opRes ℓ op under [arg@0, param@1]). The machine's custom resume focus
(Dispatch.dispatchOn custom arm) is
Comp.subst p (Comp.subst (Val.shift v) clause.2)
With clause.2 = Comp.ret w and Comp.substFrom k _ (.ret w) = .ret (Val.substFrom k _ w):
= Comp.ret (Val.subst p (Val.subst (Val.shift v) w)) -- a `ret` of a CLOSED value.
This is exactly what state/throws/transaction resume produce (ret <closed val>).
So krelS_append's resume conjunct — which demands the dispatched config be
(Sᵢ, Comp.ret r) with r₁ ~ r₂ at the perform's returner type — is satisfied with no
new convergence infrastructure. The design intent of the ret-shape (make the LR arms
tractable) is CONFIRMED, not refuted.
The three debts
| # | debt | rides | difficulty | new sub-proofs |
|---|---|---|---|---|
| 1 | compatK_handleCustom | (delegates to debt 2) | LOW — transcribe compatK_handleState | none |
| 2 | krelS_custom_reinstall | Nat.strong_induction_on on the index | MED — the riskiest arm | HandlerRel custom arm (ClauseRel) · dispatchOn_custom_isSome ×2 · clause_resume_vrel |
| 3 | dispatchOn_rename custom | structural | LOW-MED — a fork | Val/Comp.VcapFree (R-1) or the renameH/renameCls cascade (R-2) |
Debt 1 — compatK_handleCustom (crelK_fund custom arm, BinaryLR.lean:2089)
The direct analogue of compatK_handleState/compatK_handleTransaction. It is a CONGRUENCE
with no induction of its own: MINT
(g, K, handle (custom ℓ p cl) M) ↦ (g+1, handleF g (custom ℓ p cl)::K, subst (vcap g ℓ) M),
run the cap-quantified body (hbody g) through the reinstalling stack (debt 2), tail re-cast
g→g+1 via KrelS_g_cast/KrelS_eff_cast.
- Induction: none (delegated).
- Lemmas exist:
coApproxC_le_reduce,KrelS_g_cast,KrelS_eff_cast. - Lemmas to build:
krelS_custom_reinstall(debt 2). - Typing threading:
HasClauses/coverage/HasVTy [] [] p Pcome straight offHasCTy.handleCustom— mirror the state arm'shgr/hp/hpr/hrestrict/hcs/hsv. - Walls: NONE structural. Risk is entirely in debt 2. Once debt 2 exists this is a ~20-line
copy of
compatK_handleState(BinaryLR.lean:1768).
Debt 2 — krelS_custom_reinstall (krelS_refl custom arm, BinaryLR.lean:2180) — THE RISKIEST ARM
A custom ℓ p cl frame over a KrelS-related tail self-relates at every index; the resume
conjunct is supplied by GUARDED RECURSION on the index — the exact skeleton of
krelS_state_reinstall (BinaryLR.lean:1281).
- Induction:
Nat.strong_induction_onon the step index. The resume dispatch reinstallshandleF nh (custom ℓ p cl)(SAMEp, SAMEcl: v1 read-only param ⟹ the reinstall diagonal isp=p— strictly simpler than state'sput, which reinstalls a different stored value), resumesret r, andkrelS_appends onto the reinstalled frame at the dropped indexm' < m(the IH). - Lemmas exist:
krelS_handleF_intro,krelS_append(its custom cases currentlyabsurd hHRtop— see W-a),KrelS_mono,VrelK_mono. - Lemmas to build (three, all mechanical):
HandlerRelcustom arm — currentlyFalse(LR.lean:1655). Replace with theClauseRelshape (sketched in the probe):ℓ₁=ℓ₂ ∧ cl₁=cl₂ ∧ VrelK n P p₁ p₂(+ theHasClausescarrier). Custom analogue of state's∃ S, VrelK n S s₁ s₂.dispatchOn_custom_isSome×2 — one-liners likedispatchOn_state_isSome(BinaryLR.lean:1388), un-refutingkrelS_append's nested-handleF custom branch.clause_resume_vrel— turnHasClauses.cons'sHasVTy [qa,qp] [opA,P] w opRintoVrelK m' (opRes ℓ op) (subst p (subst (shift arg) w))₁ …₂. The clause value is OPEN under two binders; filling the CLOSED param + CLOSED arg makes it closed, thenvrelK_fundapplies. The double-subst is thesplit-shape (idx1-then-idx0) already used by the machine.
- Walls (named honestly):
- (W-a)
HandlerRelcustom =Falsetoday. Making it real is a DEFN change inLR.lean(NOT a frozen statement) that RIPPLES to everyHandlerRelcase-split — mainlykrelS_append's| custom => absurdarms (~4 sites,grep "HandlerRel"+ the append custom branches). MECHANICAL but WIDE — this is the bulk of the session's edit surface. - (W-b)
krelS_append's nested-handleF custom branch needs thedispatchOn_custom_isSometotality arms (currentlyabsurd hHRtop). Trivial once W-a lands. - (W-c)
clause_resume_vrel's binder-fill commutation —closeC_subst_comm-style, MECHANICAL. - NO continuation-capture wall — the ret-shape (above) means resume is
ret r, never an effectful clause needing a first-classk. This is the whole tractability payoff.
- (W-a)
Debt 3 — dispatchOn_rename custom arm (LR.lean:791)
The ONE sorry in dispatchOn_rename's custom some clause case. renameH is IDENTITY on
custom (LR.lean:480) but the resume focus contains p/clause.2, which the RHS renameC σ
would rename. The commutation holds iff renameV σ p = p and renameC σ clause.2 = clause.2
— i.e. p and every clause body are vcap-free (rename only touches vcap). Every
elaborated custom clause IS vcap-free (params/clauses are closed source values; caps enter only
via handle-mint at runtime, never inside a clause literal).
- Rename family: joins
renameV/renameC/renameH/renameK(LR.lean:452-488) + therenameC σ (Comp.subst …) = Comp.subst (renameV σ …) …commutation family. - THE FORK (ADR-input):
- (R-1)
VcapFreeside condition. DefineVal.VcapFree/Comp.VcapFree, proveVcapFree t → renameV/C σ t = t, threadVcapFree p ∧ ∀ c ∈ cl, VcapFree c.2throughdispatchOn_renameand its callers (idDispatch_rename, thestep-rename keystone). SMALL (~2 defs + 2 identity lemmas) but the side condition ripples up to the keystone's callers. - (R-2) Make
renameHTRAVERSE custom (renamep+ map-rename clause bodies). Then the commutation is the structuralrenameC_substtwin — but costs the ~15-lemmarenameH/renameClsmutual cascade (nested-inductive termination, thecapsClstwin) the PATH ledger (line 76-79) already names as "this path's re-index shape". - RECOMMENDATION: R-1 for Stage 5 (true by elaboration, small); R-2 is the ◊5+ clean-up the PATH already banks as the eventual shape.
- (R-1)
Slice plan (the grind unit executes in this order)
HandlerRelcustom arm →ClauseRel(W-a) + un-refute the ~4krelS_appendcustom branches withdispatchOn_custom_isSome(W-b). Green build gate here — this is the widest edit; land it first and isolated.clause_resume_vrel(W-c) — the substituted-clause-valueVrelKhelper.krelS_custom_reinstall(debt 2) — strong-induction copy ofkrelS_state_reinstall, using 1+2. Un-sorrys thekrelS_reflcustom arm.compatK_handleCustom(debt 1) — copy ofcompatK_handleStateover debt 2. Un-sorrys thecrelK_fundcustom arm.dispatchOn_renamecustom (debt 3, route R-1) —VcapFree+ thread the side condition. Un-sorrys theLR.leancustom arm.- Gate:
just axiomsstill 7-flagged (no new headline);lr_sound/lr_fundamentalaxiom set UNCHANGED (the W1/W2 cluster sorries remain — those are orthogonal, PATH-inc5).
Steps 1–4 are BinaryLR.lean/LR.lean HandlerRel; step 5 is LR.lean dispatchOn. All one
writer, one session. Estimated: one focused grind session (the edit surface is W-a's ripple; the
proofs are transcriptions).
ADR-inputs
- Riskiest arm: debt 2 (
krelS_custom_reinstall) — specifically W-a'sHandlerRel-custom ripple (the edit surface), not the proof (a transcription). - Is Stage 5 one grind session or more? ONE, contingent on the ret-shape holding — which the
probe CONFIRMS (
custom_resume_is_retbuilds). If a future D5 lifts the ret-shape (effectful clause bodies), THIS map is void: the continuation-capture case returns and Stage 5 becomes a multi-session first-class-kproblem. The ret-shape is load-bearing for the one-session estimate. - Debt-3 fork (R-1 vs R-2) is a real decision the grind must record — recommend R-1 now, R-2 at ◊5+.
- No frozen-statement change is needed for any debt (
HandlerRelis a proof-layer DEFN, not a Spec statement). Stage 5 does NOT touch the W1/W2lr_sound/lr_fundamentaldeferral (#15).
Ground
scratch/Stage5LRProbe.lean (this probe) · paths/PATH-inc5-lr-reindex.md (banked debts,
W1/W2) · ADR-0092 §D3-as-landed (ret-shape) · ADR-0085/0087 (custom rep) ·
BinaryLR.lean:1281 (krelS_state_reinstall = the exemplar) · BinaryLR.lean:1768
(compatK_handleState) · Dispatch.lean:177 (the custom resume focus) ·
Typing.lean:317,344,428 (HasCTy.handleCustom/HasClauses/HasStack.customF).
RESOLUTION (krnl lane, 2026-07-10) — the two residuals after s5grind, machine-checked
s5grind landed the compat cores (compatK_handleCustom, krelS_custom_reinstall,
custom_clause_resume/_of — all PROVEN, clean). What remained were TWO residuals; this
section is the DEFINITIVE map (supersedes the "LOW/SMALL" estimates above for these two).
Debt-1 residual — the crelK_fund handleCustom in-block delegation (the DESIGN PIN)
The wall (machine-checked, not budget). The crelK_fund/vrelK_fund PROOF mutual block
(BinaryLR.lean:1405, SEPARATE from the frozen VrelK/CrelK/KrelS DEF block at ~1537) is
structural recursion over the typing derivation (cases h with on HasCTy). The custom
arm needs vrelK_fund on the CLAUSE param/body, which come from the SEPARATE HasClauses
hypothesis hcl (via hasClauses_find?_typed hcl hf), NOT a sub-derivation of the scrutinee
h. The structural-recursion checker only credits recursion on sub-terms of the SCRUTINEE, so
it can NEVER see this call as decreasing.
Do-not-retry ledger (all machine-checked this session):
- (a) in-block via
custom_clause_resume_of (vf := @vrelK_fund)— REFUTED. Default budget →isDefEqtimeout (200k);set_option maxHeartbeats 1000000→fail to show termination … failed to infer structural recursion. - (c) standalone closed-value lemma — REFUTED (s5grind):
VrelK's U-clause routes thunk-typed values throughCrelK, andopRcan beU φ B, so no block-free specialization. - Fix 1 (inline
custom_clause_resume_of's body sovrelK_fund hwis syntactic) — REFUTED. Samefail to show termination … Please use termination_by:hwis still fromhcl, not the scrutinee. Inlining does not change what the checker credits.
⚠ Fix-2b as written below is MACHINE-REFUTED (krnl2, 2026-07-10; witness
Fix2bHeightRefute.lean, a scratch probe riding the lane's branch — reproduces on demand,
EXIT 1 by design) — kept for the do-not-retry record; see the amendment after the slice plan
for the surviving directions. Root cause the pin missed: HasVTy/HasCTy/
HasClauses are Prop-valued (Typing.lean:103/138/344), so ANY Nat-height over them is
large elimination (Prop → Type), which Lean forbids:
- hand-rolled
htC : HasCTy → Nat→error(nested.lean.propRecLargeElim): "recursor HasCTy.casesOn can only eliminate into Prop" the moment a match binds constructor data; - auto-
sizeOf→ the degenerate PropSizeOfinstance (sizeOf = 0for every Prop), so every decreasing goal is0 < 0. The pin's "TRYsizeOffirst" hedge assumedsizeOfcarries derivation structure; on a Prop it is definitionally trivial. Do-not-retry: derivation-height over these Props is structurally impossible — noset_optionrescuespropRecLargeElim.
THE FIX — Fix-2b, height-indexed (REFUTED — the original text):
- Height functions over the mutual
HasVTy/HasCTy/HasClausesderivation:htV : HasVTy … → Nat,htC : HasCTy … → Nat,htCl : HasClauses … → Nat, each= 1 + max(children heights). CRUX:htC (handleCustom hcl … hM …)must strictly exceedhtCl hcl(so the clause derivationshasClauses_find?_typedextracts are at strictly smaller height) ANDhtC hM. (Lean's auto-sizeOfon the mutual inductive may already give this — TRYsizeOffirst; only hand-rollhtCifsizeOf's cross-mutual accounting doesn't creditHasClausessub-derivations. Build-arbitrate which.) - Height-indexed twins proven by
induction k(well-founded onk : Nat, NOT structural on the derivation — this is what dodges the wall):In thevrelK_fund_at : ∀ k, HasVTy γ Γ v A → htV h ≤ k → ∀ n δ₁ δ₂, EnvRelK n Γ δ₁ δ₂ → VrelK n A (closeV δ₁ v) (closeV δ₂ v) crelK_fund_at : ∀ k, HasCTy γ Γ c e B → htC h ≤ k → ∀ n δ₁ δ₂, EnvRelK n Γ δ₁ δ₂ → CrelK n B e (closeC δ₁ c) (closeC δ₂ c)handleCustomarm, thevrelK_fundcalls on the clause param/body invokevrelK_fund_at (k-1)at their strictly-smaller heights (htV hp < htC h,htV hw < htC hthroughhtCl), and the recursive body call iscrelK_fund_at (k-1) hM. Thek=0base is vacuous (htC h ≤ 0is impossible since everyhtC ≥ 1). - Post-block, the FROZEN twins recover byte-identically (this is what keeps
Spec.lean:248lr_fundamental h := crelK_fund huntouched):The type oftheorem crelK_fund (h : HasCTy γ Γ c e B) : ∀ n δ₁ δ₂, EnvRelK n Γ δ₁ δ₂ → CrelK n B e (closeC δ₁ c) (closeC δ₂ c) := crelK_fund_at (htC h) h (le_refl _) -- and likewise vrelK_fund := vrelK_fund_at (htV h) h (le_refl _)crelK_fundis BYTE-IDENTICAL to the current one (BinaryLR.lean:1453), so Spec.lean:248,custom_clause_resume(:1669), andkrelS_reflare all untouched.
Which arms recurse at strictly-smaller height: every arm that today calls vrelK_fund/
crelK_fund recursively — vthunk (crelK_fund on the thunk body), inl/inr/pair/fold
(vrelK_fund on payloads), the handle* arms (crelK_fund hM), and the NEW custom arm
(vrelK_fund on hp/hw via htCl, crelK_fund hM). All are structural children ⟹ strictly
smaller htC/htV/htCl ⟹ within k-1.
Frozen-DEF-block untouched: the VrelK/CrelK/KrelS DEF block (:1537, measure
(n,_,_,sizeOf _)) is NOT touched — Fix-2b only restructures the PROOF block's recursion from
implicit-structural to explicit-k-induction. No set_option on the frozen block; the frozen
block's 200k-budget heartbeat inference is not perturbed (a separate block).
Slice plan for the grind (fresh unit):
- Define
htV/htC/htCl(or confirmsizeOfsuffices) + thehtC (handleCustom) > htCl hcllemma. Green gate. - State
vrelK_fund_at/crelK_fund_at; port the NON-custom arms verbatim (they're the current arms with_at (k-1)on recursive calls + thehtX child < htX h ≤ kside-goals byomega). Green gate — this is the bulk, mechanical. - Add the custom arm using the (proven)
compatK_handleCustom+ an inline/_ofhclausenow callingvrelK_fund_at (k-1)on the clause derivations. Un-sorries the custom arm. - Post-block:
crelK_fund/vrelK_fund=_at (htX h) h (le_refl _). Byte-identical type; Spec.lean:248 rebuilds unchanged. - Gate:
just axioms—lr_fundamental/lr_soundLOSE nothing but the custom-armsorryAxcontribution (the W1/W2 cluster sorries remain, orthogonal); 16 clean headlines byte-unchanged.
Budget note: this is a multi-session grind (the _at port + the height lemmas). The manager's
call is whether it runs on the current unit's budget or a fresh unit — this pin is written so a
FRESH unit can grind it cold.
AMENDMENT (manager, 2026-07-10) — Fix-2b refuted; the surviving directions, probe-ordered
The height-function mechanism above died at Slice 1's viability probe (see the ⚠ block) — the
refute-first discipline catching a bad pin before the ~670-line port. The _at-twin SHAPE
(k-induction + post-block byte-identical recovery of the frozen crelK_fund type, Spec.lean:248
untouched) survives; only the MEASURE must move off the Prop derivation. Two candidates, ruled
into this probe order:
(A) Term-measured induction — spike FIRST, falsifiers before port. Comp/Val/Handler
are Type (IR.lean:113); the clause bodies and M are genuine sub-TERMS of the scrutinee
Comp.handle (Handler.custom ℓ p clauses) M, and the LR fundamental recurses on derivations of
the ORIGINAL (pre-substitution) term — closing substitutions ride EnvRelK, so the recursion
targets are subterms. Index _at by sizeOf (c : Comp) (or a bespoke measure). TWO
pre-registered falsifiers, checked in this order BEFORE any porting:
- Same-term arms kill it outright: if ANY
HasCTy/HasVTyconstructor types the SAME term via a recursive premise on that same term (subsumption / row-weakening shape), the term measure does not decrease there and (A) is dead — grep the constructors first; this is a minutes-check. - List descent: does the measure strictly decrease from the
handleterm into a clause bodywnested underclauses : List (OpId × Comp)? Auto-sizeOfmay not credit the List payload; a bespokeComp.measurecounting into handler clauses is the expected fix — but build-arbitrate before trusting it.
(C) Fuel-indexed judgment copy — the KNOWN-VIABLE fallback. Define HasCTyN : Nat → …
(+ HasVTyN/HasClausesN), a fuel-indexed mirror of the judgment (constructors demand fuel
n for sub-derivations, conclusion at n+1), plus the bridge
HasCTy … ↔ ∃ n, HasCTyN n … — both directions eliminate Prop into Prop, so NO large-elimination
wall exists anywhere in this route by construction. Induct _at on the fuel. Cost: mechanical
duplication of the judgment's constructors (or the fragment the proof block touches) + the
bridge lemmas — bigger diff than (A), zero type-theoretic risk. Frozen statements untouched
(same post-block recovery).
(B) Statement-level fuel on the frozen twins — REJECTED unless (A) and (C) both die. Touches
frozen statements (Spec.lean:248 chain), needs a frozen-statement ruling + kernel-engineer
review; the whole point of the _at shape is to avoid this.
Debt-3 residual — the R-1 dispatchOn_rename custom sorry is in DEAD CODE
The Debt-3 R-1 plan above ("thread VcapFree through dispatchOn_rename → idDispatch_rename
→ the step-rename keystone") is VOID: that keystone chain is dead code. Ref-verified
(krnl, 2026-07-10, clean tree): the chain dispatchOn_rename → idDispatch_rename → step_rename → run_rename → run_rename_converges → run_bump_converges has ZERO live callers — 0 refs outside
LR.lean; the terminus run_bump_converges has no caller at all (only its def + one prose
comment at :1961 that says "the OLD frozen-counter form"). The route-1 crelK_ret refactor
orphaned it (LR.lean:515: "crelK_ret (route-1 form) bridges … no Canonical/CapsBelow/run_bump").
The by sorry at LR.lean:1018 sits inside this dead chain. Correct move: DELETE the 6 dead
chain lemmas (removes the sorry + dead code) rather than thread a premise into lemmas nothing
consumes. Deletion is surgical (the 6 are interleaved with the LIVE renameCfg/bumpσ/
CapsBelow/renameK_capsBelow machinery — 102 live refs outside LR — so delete ONLY the 6
theorems + any helper uniquely consumed by them; build + axiom-diff arbitrates). Gated behind an
operator delete-vs-thread ruling (census-adjacent: the deletion may remove a sorryAx from a
flagged headline, which shrinks the flagged set — a ledger event).