Skip to content

proof(coq): add step_R_eq_or_touches_region region-invariance lemma#114

Merged
hyperpolymath merged 1 commit into
mainfrom
proof-debt/ephapax-preservation-lemmas
May 20, 2026
Merged

proof(coq): add step_R_eq_or_touches_region region-invariance lemma#114
hyperpolymath merged 1 commit into
mainfrom
proof-debt/ephapax-preservation-lemmas

Conversation

@hyperpolymath
Copy link
Copy Markdown
Owner

Summary

Adds the region-invariance lemma for the step relation — the missing piece of metatheoretic infrastructure that should eventually close ~10 of preservation's remaining 22 goals (the congruence cases where sibling-typing transfer is the only blocker).

What's in

Inductive touches_region : expr -> Prop

19 constructors, one per step-reducible compound form:

  • TR_here — base case (ERegion r e)
  • TR_StringConcat1, TR_StringConcat2 — congruence into the two arms
  • TR_Let, TR_LetLin, TR_App1, TR_App2, TR_If, TR_Pair1, TR_Pair2, TR_Fst, TR_Snd, TR_Inl, TR_Inr, TR_Case, TR_StringLen, TR_Borrow, TR_Drop, TR_Copy — congruence into each Step1/Step variant

Lemma step_R_eq_or_touches_region (Qed)

Lemma step_R_eq_or_touches_region :
  forall mu R e mu' R' e',
    (mu, R, e) -->> (mu', R', e') ->
    R = R' \/ touches_region e.

For any step from (mu, R, e) to (mu', R', e'), either the region environment is preserved (the left disjunct), or the expression contains a region operation at a step-reducible position (the right disjunct).

Proof by induction on the step, with the same remember + revert structure used inside preservation itself. Three case classes:

  1. Atom steps (R kept literally in the constructor's conclusion — S_StringNew, S_StringConcat, S_StringLen, S_Let_Val, S_LetLin_Val, S_App_Fun, S_If_True, S_If_False, S_Fst, S_Snd, S_Case_Inl, S_Case_Inr, S_Drop, S_Copy): close by left; reflexivity.

  2. Region steps (S_Region_Enter, S_Region_Exit, S_Region_Step): close by right; apply TR_here.

  3. Congruence steps (S_X_StepK): close by IH on the inner step + appropriate TR_X constructor.

What's not in

The tactic that would apply this lemma inside preservation to actually close goals. The intended pattern is documented as a comment-sketch:

pose proof (step_R_eq_or_touches_region _ _ _ _ _ _ Hstep) as Hdis.
destruct Hdis as [HeqR | HTR].
- subst.  (* unifies R = R' so sibling typing transfers *)
  edestruct (IHHstep _ _ _ _ _ _ eq_refl eq_refl _ _ _ HtypingOfInnerExpr)
    as [Gout Hout].
  eexists. econstructor; eauto.
- (* touches_region — needs region weakening, separately tracked *)
  ...

In isolation the pose proof step works (verified by inserting a diagnostic). In match goal with [H : step _ _ |- _] => ... form across all goals via all: try solve [...], the subsequent destruct/subst/IH chain hits ltac edge cases on these specific goal shapes. The lemma remains usable for future per-case manual proofs — that's the next-session deliverable.

Why land this separately

The lemma is reusable infrastructure regardless of whether the wholesale tactic ever lands. Future closures of the 22 remaining goals will reach for step_R_eq_or_touches_region per-case anyway. Banking the Qed-closed lemma now means the next session opens with one fewer prerequisite to write.

Verification

$ coqc -Q . Ephapax Semantics.v
(builds clean; Admitted preservation unchanged at 22 goals)

Refs

  • standards#124 (proof-debt audit epic)
  • ephapax#102 (910 → 29 via remember-cfg)
  • ephapax#104 (PROOF STATUS docs correction)
  • ephapax#106 (29 → 22 via universal-IH revert)

Test plan

  • coqc -Q . Ephapax Semantics.v builds clean
  • New Qed step_R_eq_or_touches_region at line 3293
  • Preservation count unchanged at 22 (no regression)
  • CI green

🤖 Generated with Claude Code

Lands a fresh Coq lemma (Qed, ~80 LOC) capturing the structural fact
that the step relation only changes the region environment [R] when
the expression contains a region operation at a step-reducible
position. This is the missing piece for closing the 10 congruence
goals out of preservation's remaining 22 — the sibling-typing
premise in each can be transferred from the pre-step R to the
post-step R' precisely when R = R' (which the lemma's left
disjunct guarantees for non-region step paths).

## What's landed

  - `Inductive touches_region : expr -> Prop` — 19 constructors,
    one per step-reducible compound form (TR_StringConcat{1,2},
    TR_Let, TR_LetLin, TR_App{1,2}, TR_If, TR_Pair{1,2}, TR_Fst,
    TR_Snd, TR_Inl, TR_Inr, TR_Case, TR_StringLen, TR_Borrow,
    TR_Drop, TR_Copy, plus the base case TR_here for ERegion).

  - `Lemma step_R_eq_or_touches_region` (Qed) — for any step
    (mu, R, e) -->> (mu', R', e'), either R = R' or e satisfies
    touches_region. Proved by induction on step with the same
    remember + revert structure used in `preservation` itself
    (so the IHs are clean over the inner step's config).

## What's NOT in this PR

  - Actually applying the lemma inside `preservation` to close
    goals. The intended tactic uses `pose proof + destruct +
    subst + IH match`, but in `match goal` form on the specific
    22-goal shapes the application hits ltac edge cases I haven't
    pinned. The lemma is a usable building block for future
    per-case manual proofs; the wholesale `all: try solve` tactic
    application is left as an inline comment-sketch for the next
    pass.

## Verification

  $ coqc -Q . Ephapax Semantics.v
  (builds clean; Admitted preservation unchanged at 22 goals)

Refs standards#124, ephapax#102, ephapax#104, ephapax#106.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@hyperpolymath hyperpolymath merged commit 19f37e8 into main May 20, 2026
0 of 8 checks passed
@hyperpolymath hyperpolymath deleted the proof-debt/ephapax-preservation-lemmas branch May 20, 2026 23:25
hyperpolymath added a commit that referenced this pull request May 21, 2026
…ofs (#116)

## Summary

**Preservation existential count: 22 → 12** (10 goals closed).

Builds on the region-invariance lemma `step_R_eq_or_touches_region` (PR
#114) by adding per-step-constructor manual proof scripts inside
`Theorem preservation`.

## Closed (10 step constructors)

| Constructor | Closure technique |
|---|---|
| `S_Let_Val`, `S_LetLin_Val` | β-reduction via `subst_preserves_typing`
+ `value_context_unchanged` |
| `S_App_Fun` | Inversion of `ELam` typing + value-context invariance on
arg |
| `S_If_True`, `S_If_False` | Branch typing under `VBool`'s
unchanged-ctx via `value_context_unchanged` |
| `S_Fst`, `S_Snd` | `EPair` inversion → child typing match |
| `S_Case_Inl`, `S_Case_Inr` | `EInl` / `EInr` typing inversion + body
substitution |
| `S_Copy` | `T_Pair` on non-linear value with equal pre/post context |

## Open (12 remaining)

**11 congruence cases** (`S_StringConcat_Step1/2`, `S_StringLen_Step`,
`S_Let_Step`, `S_LetLin_Step`, `S_App_Step1/2`, `S_If_Step`,
`S_Pair_Step1/2`, `S_Case_Step`).

Root cause: the **linearity-context drift** I diagnosed earlier — even
after `step_R_eq_or_touches_region` puts us in the `R = R'` branch, the
IH's output context (a Skolem variable from `edestruct`) doesn't unify
with the sibling premise's pre-context. So `eapply T_Foo; [exact Hout |
exact Hsibling]` fails on the middle-context unification.

The tactic-block scaffolding for these 11 cases is in place (poses
`step_R_eq_or_touches_region`, dispatches the LEFT branch, specialises
`IHHstep`), with the final reconstruction silently failing — they fall
through to `Admitted`. The right next step is **lemma B**: a "step
preserves output linearity context" or context-transfer re-typing via
the existing `typing_ctx_transfer`/`type_determinacy` machinery.

**1 region case** (`S_Region_Step` + `T_Region_Active`): the documented
language-design blocker — region-env weakening for non-values. Tracked
in `formal/PRESERVATION-HANDOFF.md`.

## Verification

```
$ coqc -Q . Ephapax Semantics.v
(builds clean; Admitted preservation now at 12 existentials)
```

## Refs

- `standards#124` (proof-debt audit epic)
- `ephapax#102` (910 → 29 via remember-cfg)
- `ephapax#106` (29 → 22 via universal-IH revert)
- `ephapax#114` (region-invariance lemma — used here)

## Cumulative

| State | Open goals |
|---|---:|
| Pre-#102 | 910 |
| Post-#102 | 29 |
| Post-#106 | 22 |
| **Post this PR** | **12** |

98.7% reduction across the day from start of campaign.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hyperpolymath added a commit that referenced this pull request May 21, 2026
…122)

Closure sweep after PR #116 took preservation from 22 → 12 open goals
and PR #121 landed Phase 1 scaffold. Updates the preservation-count
claims across docs to reflect 12, and adds a CHANGELOG entry summarising
the full 2026-05-20 → 2026-05-21 reduction campaign.

## Files updated

| File | What changed |
|---|---|
| `README.adoc` | Coq formal-foundations paragraph; reduction chain now
lists all 8 PRs through #121; clear pointer to ROADMAP closure plan |
| `ROADMAP.adoc` | Formal-proof status table row (`12 open goals`); v0.1
blocker entry; v1.0 status; 97.6% → 98.7% reduction figure |
| `EXPLAINME.adoc` | Coq theorem table; reduction-story bullets extended
through #114, #116, #121 |
| `CHANGELOG.md` | New "Proof state" + "Documentation" sections under
`[Unreleased]` tracking the full PR chain (#92, #102, #104, #106, #114,
#115, #116, #117, #121) + the Idris2 totality campaign (#89#100) +
doc/wiki refresh (#113 + wiki) |

## Companion wiki update

`Home.md`, `Proof-status.md`, `What-can-go-wrong.md` updated in the wiki
repo with the same 22 → 12 figures + extended reduction story. Pushed
separately to wiki master.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant