Skip to content

Commit 9f76e67

Browse files
proof(L1.G): convert Axiom region_liveness_at_split_l1 to Lemma with 1 narrow admit (#178)
## Summary Replaces the opaque `Axiom region_liveness_at_split_l1` with a structurally-proved `Lemma … Admitted.` that closes **24 of 26 inductive cases concretely**, leaving exactly 1 admit at the documented counterexample sub-case (T_Region_Active_L1 with binder = rv). The lemma's STATEMENT is unchanged — still universal `forall e`, still false in the residual sub-case. **This PR is about transparency of proof debt, not soundness.** The remaining admit accepts the same false sub-case the Axiom did. But future closure work now has a clearly identified target rather than an opaque hide-all. ## Closure pattern (24/26 cases proved concretely) | Case class | Count | Discharge | |---|---|---| | R-unchanged base (T_Unit, T_Bool, T_I32, T_Var_*, T_Loc, T_StringNew, T_Lam, T_Borrow, T_Borrow_Val) | 10 | `try assumption` (IH-free; hypothesis [In rv R] = goal) | | Compound (T_StringConcat, T_StringLen, T_Let, T_LetLin, T_App, T_Pair, T_Fst, T_Snd, T_Inl, T_Inr, T_Drop, T_Copy) | 12 | IH chain | | Branch-converging (T_Case, T_If) | 2 | IH through R1 to R_final | | T_Region_L1 (fresh binder, ~In r R) | 1 | `remove_first_L1_count_other` (r ≠ rv from ~In r R ∧ In rv R) | | T_Region_Active_L1 r ≠ rv | 1 (sub-case) | same as T_Region_L1 | | **T_Region_Active_L1 r = rv** | **1 (sub-case)** | **`admit.` — GENUINELY FALSE** | The remaining admit is at `formal/Semantics_L1.v:1055`. Documented source-level counterexample: ``` ERegion rv (EI32 5) : TBase TI32 -| [] at R = [rv] ``` The rule pops the only `rv` from `R_body`, so `In rv R = True` but `In rv R' = False`. ## Closure paths forward (in-file + proof-debt.adoc) - **(i)** Restate with a `no_region_active_pop_of rv e` side condition and discharge at the 9 call sites in `subst_typing_gen_l1` (smallest step, consistent with §4.8 closure approach (b)). - **(ii)** Multi-set `region_env` (substantial L1 redesign). - **(iii)** Weaker contextual signature. (i) is the smallest step and the recommended next slice. ## Side update: `docs/proof-debt.adoc` - Refreshed line numbers post-#170 + post-this-PR (entries now point to actual current positions in `Semantics_L1.v`). - Replaced the Axiom L903 entry with the Lemma L998 + admit L1055 + Admitted L1068 trio. - Echo.v K entry updated to note PR #173's K-freedom closure (no more current escape hatch in Echo.v). ## Branch base Stacked off `proof/l1-region-threading-design` (head `f03d7e7`, post-#173). ## Test plan - [x] `coqc 8.18.0` builds `Semantics_L1.v` cleanly - [x] Clean full-project rebuild passes — 9 .v files including Echo.v, Modality.v, TypingL2.v - [x] `Counterexample.v` still Qed (regression test for the L1 design counterexample) - [x] 0 Axiom declarations in `Semantics_L1.v` (down from 1) - [x] `Print Assumptions subst_preserves_typing_l1` still mentions `region_liveness_at_split_l1` (now as an opaque Admitted-Lemma, not Axiom — same logical state) - [x] `proof-debt.adoc` line numbers cover all 9 current escape-hatch positions (8 admits/Admitted + 1 Lemma-name marker) - [ ] CI green on the next #153 cycle ## Caveats - This is **not a soundness improvement**. The lemma's statement is universal, the residual sub-case is genuinely false, and the `Admitted.` makes Coq accept it the same way `Axiom` did. - The improvement is **proof-debt visibility**: 24 cases now have concrete witnesses, the residual obstacle has a clear narrow definition, and closure work can attack just one specific structural sub-case. Refs PR #153, PR #169, PR #170, PR #173, PRESERVATION-DESIGN.md §4.8 + §4.8.1 + §5.1. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
1 parent f03d7e7 commit 9f76e67

2 files changed

Lines changed: 137 additions & 38 deletions

File tree

docs/proof-debt.adoc

Lines changed: 48 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -141,31 +141,46 @@ sequenced under the four-layer redesign plan.
141141
a documentation comment, not a Coq `Admitted.` declaration.
142142
Listed here for the scanner's substring match; no escape hatch
143143
at this position.
144-
* **`formal/Semantics_L1.v:466`** — `admit.` inside
144+
* **`formal/Semantics_L1.v:471`** (formerly L466) — `admit.` inside
145145
`region_shrink_preserves_typing_l1_gen` (T_Region_Active_L1
146-
shadowed case). Blocked on a list-vs-multiset structural
147-
mismatch with three documented closure approaches (lemma's
148-
design note, lines 458-465).
149-
* **`formal/Semantics_L1.v:488`** — `Admitted.` capping the
150-
`region_shrink_preserves_typing_l1_gen` lemma. Closes when its
151-
one residual internal admit (above) closes.
152-
* **`formal/Semantics_L1.v:903`** — `Axiom region_liveness_at_split_l1`.
153-
Documented in-file (lines 882-902) as understated: the universal
154-
`forall e` form is not true, requires either statement
155-
strengthening + 9-call-site propagation OR a global invariant via
156-
approach (a) (strengthen `T_Var_Lin_L1`). Investigation 2026-05-27
157-
concluded approach (c) is L2-gated via effect-typed `TFun`.
158-
* **`formal/Semantics_L1.v:1258`** — `admit.` for `S_StringConcat_Step2`
159-
in `preservation_l1`. Needs a `step_pop_disjoint_from_type_l1`
160-
lemma — tractable, deferred to L1.G slice.
161-
* **`formal/Semantics_L1.v:1344`** — `admit.` for `S_App_Step2` in
162-
`preservation_l1`. Lambda-rigidity gap per
163-
`PRESERVATION-DESIGN.md` §4.8 — explicitly L2/L3-gated.
164-
* **`formal/Semantics_L1.v:1385`** — `admit.` for `S_Pair_Step2` in
165-
`preservation_l1`. Same lambda-rigidity gap as `S_App_Step2`.
166-
* **`formal/Semantics_L1.v:1515`** — `Admitted.` capping
167-
`preservation_l1`. Closes when its three internal admits
168-
(1258 / 1344 / 1385) close — L2-gated.
146+
shadowed case). Blocked on a list-vs-multiset structural mismatch
147+
with three documented closure approaches (lemma's design note).
148+
* **`formal/Semantics_L1.v:493`** (formerly L488) — `Admitted.`
149+
capping the `region_shrink_preserves_typing_l1_gen` lemma. Closes
150+
when its one residual internal admit (above) closes.
151+
* **`formal/Semantics_L1.v:998`** (formerly `Axiom` at L903 / L977) —
152+
`Lemma region_liveness_at_split_l1`. Now a structurally-proved
153+
lemma rather than an opaque axiom: 24 of 26 inductive cases close
154+
with concrete proof bodies (8 R-unchanged cases auto-discharge;
155+
10 compound cases route through IH chains; 1 region case closes
156+
via [remove_first_L1_count_other]; 1 sub-case admitted — see
157+
L1055). Statement still universal and still false in the L1055
158+
sub-case; closure requires either (i) statement strengthening with
159+
a `no_region_active_pop_of rv e` side-condition, (ii) a multi-set
160+
region environment, or (iii) a weaker contextual signature. All
161+
three are L1 follow-up; (i) is the smallest step.
162+
* **`formal/Semantics_L1.v:1055`** — `admit.` inside
163+
`region_liveness_at_split_l1`'s `T_Region_Active_L1` `r = rv`
164+
sub-case. GENUINELY FALSE: counterexample at the source level is
165+
`ERegion rv (EI32 5)` typed at `R = [rv]` — the rule pops the only
166+
`rv` from `R_body`, so `In rv R` holds but `In rv R'` does not.
167+
* **`formal/Semantics_L1.v:1068`** — `Admitted.` capping
168+
`region_liveness_at_split_l1`. Closes when the L1055 sub-case
169+
closes via any of (i)/(ii)/(iii).
170+
* **`formal/Semantics_L1.v:1428`** (formerly L1258) — `admit.` for
171+
`S_StringConcat_Step2` in `preservation_l1`. Needs a
172+
`step_pop_disjoint_from_type_l1` lemma — tractable, deferred to
173+
L1.G slice.
174+
* **`formal/Semantics_L1.v:1514`** (formerly L1344) — `admit.` for
175+
`S_App_Step2` in `preservation_l1`. Lambda-rigidity gap per
176+
`PRESERVATION-DESIGN.md` §4.8 + §5.1 — explicitly L2-gated via
177+
effect-typed `TFun`.
178+
* **`formal/Semantics_L1.v:1555`** (formerly L1385) — `admit.` for
179+
`S_Pair_Step2` in `preservation_l1`. Same lambda-rigidity gap as
180+
`S_App_Step2`.
181+
* **`formal/Semantics_L1.v:1685`** (formerly L1515) — `Admitted.`
182+
capping `preservation_l1`. Closes when its three internal admits
183+
(1428 / 1514 / 1555) close — L2-gated.
169184

170185
All eight sites are tracked in the L1 closure plan (PRs
171186
https://github.com/hyperpolymath/ephapax/pull/155[#155]–https://github.com/hyperpolymath/ephapax/pull/163[#163]).
@@ -174,18 +189,15 @@ explicitly NOT scheduled before
174189
https://github.com/hyperpolymath/ephapax/pull/153[#153] lands on
175190
`main`.
176191

177-
=== `formal/Echo.v` — L3 layer's documented K dependency
178-
179-
* **`formal/Echo.v` `mode_le_trans` + `degrade_mode_comp`** — pull
180-
in Coq's standard `Eqdep.Eq_rect_eq.eq_rect_eq` (K / UIP) axiom
181-
via `dependent destruction`. The Agda upstream
182-
(`echo-types/proofs/agda/EchoLinear.agda`) is `--safe --without-K`;
183-
the rest of the Ephapax Coq codebase is K-free. Tracked closure:
184-
rewrite both with raw dependent pattern matching using motive
185-
tricks (see `formal/Modality.v`'s `modality_le_trans` for the
186-
K-free template). All other 10+ lemmas in `Echo.v` (including
187-
the headline `no_section_collapse_to_residue`) are closed under
188-
the global context — zero axioms.
192+
=== `formal/Echo.v` — L3 layer K dependency CLOSED 2026-05-27
193+
194+
* The previously-documented K dependency in `mode_le_trans` +
195+
`degrade_mode_comp` (via `dependent destruction`) was discharged
196+
in PR #173 using the K-free motive-trick template from
197+
`formal/Modality.v`. **All 16+ lemmas in `Echo.v` are now closed
198+
under the global context** — zero K, zero UIP, zero axioms.
199+
Entry kept here for historical visibility; no current escape
200+
hatch in `Echo.v`.
189201

190202
=== `idris2/src/Main.idr:21` — `%default partial`
191203

formal/Semantics_L1.v

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -973,12 +973,99 @@ Proof. intros. apply T_Loc_L1. assumption. Qed.
973973
and consistent with the side-conditioning the design doc lists as
974974
closure approach (b). The strengthening landed in this PR is
975975
independently valuable (closes the §4.8 soundness gap) and is a
976-
prerequisite for (i) discharging cleanly at call sites. *)
977-
Axiom region_liveness_at_split_l1 :
976+
prerequisite for (i) discharging cleanly at call sites.
977+
978+
=== 2026-05-27 partial discharge: Axiom → Lemma with 1 narrow admit ===
979+
980+
[Axiom] is replaced with [Lemma .. Admitted.] below. The proof
981+
body now structurally encodes the case-by-case analysis the
982+
header describes:
983+
984+
- 23 base + compound cases close mechanically (R unchanged or
985+
threaded through sub-derivations via IH).
986+
- T_Region_L1 (fresh binder) closes via [remove_first_L1_count_other]:
987+
[~ In r R ∧ In rv R] gives [r ≠ rv], so the pop preserves rv.
988+
- T_Region_Active_L1 (re-entry binder):
989+
* Sub-case [r ≠ rv]: same [remove_first_L1_count_other] argument.
990+
* Sub-case [r = rv]: the GENUINELY-FALSE case. One [admit] sits
991+
here, with a comment quoting the counterexample at the source
992+
level (ERegion rv (EI32 5)).
993+
994+
This converts an opaque universal Axiom into a Lemma with one
995+
explicitly-narrow admit. The remaining admit is a structural
996+
obligation tied to a documented counterexample, addressable by
997+
any of the three closure paths (i)/(ii)/(iii) above. *)
998+
Lemma region_liveness_at_split_l1 :
978999
forall R G e T R' G' rv,
9791000
R; G |=L1 e : T -| R'; G' ->
9801001
In rv R ->
9811002
In rv R'.
1003+
Proof.
1004+
intros R G e T R' G' rv Ht.
1005+
induction Ht; intros Hin; try assumption.
1006+
(* Compound cases — thread rv through sub-derivations via IH. *)
1007+
- (* T_StringConcat_L1: R -> R1 -> R2 *)
1008+
apply IHHt2. apply IHHt1. assumption.
1009+
- (* T_StringLen_L1: R -> R' *)
1010+
apply IHHt. assumption.
1011+
- (* T_Let_L1: R -> R1 -> R2 *)
1012+
apply IHHt2. apply IHHt1. assumption.
1013+
- (* T_LetLin_L1: R -> R1 -> R2 *)
1014+
apply IHHt2. apply IHHt1. assumption.
1015+
- (* T_App_L1: R -> R1 -> R2 *)
1016+
apply IHHt2. apply IHHt1. assumption.
1017+
- (* T_Pair_L1: R -> R1 -> R2 *)
1018+
apply IHHt2. apply IHHt1. assumption.
1019+
- (* T_Fst_L1: R -> R' *)
1020+
apply IHHt. assumption.
1021+
- (* T_Snd_L1: R -> R' *)
1022+
apply IHHt. assumption.
1023+
- (* T_Inl_L1: R -> R' *)
1024+
apply IHHt. assumption.
1025+
- (* T_Inr_L1: R -> R' *)
1026+
apply IHHt. assumption.
1027+
- (* T_Case_L1: R -> R1 -> R_final (both branches converge) *)
1028+
apply IHHt2. apply IHHt1. assumption.
1029+
- (* T_If_L1: R -> R1 -> R2 (both branches converge) *)
1030+
apply IHHt2. apply IHHt1. assumption.
1031+
- (* T_Region_L1 (fresh): premise ~ In r R, body input r::R, output
1032+
remove_first_L1 r R_body. Since ~In r R but In rv R, r ≠ rv;
1033+
remove_first_L1 preserves rv. *)
1034+
assert (Hne : r <> rv).
1035+
{ intro Heq. subst rv. apply H. exact Hin. }
1036+
(* Body's input is r :: R; In rv R lifts to In rv (r :: R) on the right *)
1037+
assert (Hin_body_in : In rv (r :: R)) by (right; exact Hin).
1038+
pose proof (IHHt Hin_body_in) as HinRbody.
1039+
(* Goal: In rv (remove_first_L1 r R_body) *)
1040+
apply (count_occ_In string_dec).
1041+
pose proof (remove_first_L1_count_other r rv R_body Hne) as Heq.
1042+
unfold cnt in Heq. rewrite Heq.
1043+
apply (count_occ_In string_dec). exact HinRbody.
1044+
- (* T_Region_Active_L1 (re-entry): premise In r R, body input R,
1045+
output remove_first_L1 r R_body. Two sub-cases: *)
1046+
destruct (string_dec r rv) as [Heq|Hne].
1047+
+ (* r = rv: GENUINELY FALSE. Counterexample at the source level:
1048+
ERegion rv (EI32 5) types at R = [rv] via T_Region_Active_L1
1049+
(T = TBase TI32, ~In rv (free_regions T) holds vacuously,
1050+
body R_body = [rv]); the rule pops the only rv, R' = [] —
1051+
so In rv R = True but In rv R' = False.
1052+
1053+
This is the residual structural obstacle described in the
1054+
header. Closing it requires one of (i)/(ii)/(iii). *)
1055+
admit.
1056+
+ (* r ≠ rv: same shape as T_Region_L1. *)
1057+
pose proof (IHHt Hin) as HinRbody.
1058+
apply (count_occ_In string_dec).
1059+
pose proof (remove_first_L1_count_other r rv R_body Hne) as Heq.
1060+
unfold cnt in Heq. rewrite Heq.
1061+
apply (count_occ_In string_dec). exact HinRbody.
1062+
(* T_Borrow_L1, T_Borrow_Val_L1: both have R unchanged — auto-
1063+
discharged by [try assumption] above. *)
1064+
- (* T_Drop_L1: R -> R' *)
1065+
apply IHHt. assumption.
1066+
- (* T_Copy_L1: R -> R' *)
1067+
apply IHHt. assumption.
1068+
Admitted.
9821069

9831070
(** Generalized substitution: at depth [k] for a linear value [v].
9841071
Mirrors legacy [subst_typing_gen]. The only L1-specific gap is the

0 commit comments

Comments
 (0)