Skip to content

Commit 18e3e3c

Browse files
proofs(coq): close build-oracle cascade + re-enable verify-proofs (closes #49) (#55)
* proofs(coq): partial closure of build-oracle cascade (refs #49) Fixes the first wave of errors uncovered while attempting to re-enable the verify-proofs CI oracle. Issue #49 catalogued 5 errors; this PR closes those + several deeper cascading failures in the same modules. Closed: - file_content_operations.v:133 redundant rewrite of Hntype after eqn: destruct already substituted - file_content_operations.v:137,140 exact (Hcontra eq_refl) replaced with destruct (False has no constructors) - file_content_operations.v:178 destruct (node_type node) missing eqn: (no substitution into RHS match) - file_content_operations.v:272 injection on un-reduced match — restructured with assert + destruct - filesystem_model.v added has_read_permission (referenced by copy_move_operations:38, never defined) - copy_move_operations.v reordered is_prefix definition before its use at line 62 Verified locally: filesystem_model.v, file_operations.v, file_content_operations.v compile against coqc 8.18.0. Remaining errors past line ~110 of copy_move_operations.v and downstream in filesystem_composition.v are tracked as continuation of #49. The verify-proofs CI job will be re-enabled in a follow-up PR once the full module set compiles end-to-end. Refs #49, #47. * proofs(coq): close build-oracle cascade + re-enable verify-proofs (closes #49) Completes the verify-proofs CI guard from issue #49. After PR #50's first wave (file_content_operations + filesystem_model + copy_move is_prefix reorder), the cascade ran deeper than the 5 enumerated errors: Fixed (extends #49): * copy_move_operations.v: 6 contradiction-direction + reflexivity-via-Hfile fixes (copy_file_preserves_source, move_creates_destination, move_preserves_content, move_reversible, copy_preserves_other_paths, copy_then_move). * filesystem_composition.v: 4 issues from #49 closed (Lia import, operation_sequence_reversible rewrite direction + apply_sequence_app helper, reversible_creates_CNO stale app_nil_r, path_prefix_app_invert arg shape via Hpre_path save). Plus 4 not-in-#49: well_formed_ancestor_exists (absurd typo + IHn vs Hind scope + symmetry), mkdir_preserves_well_formed rewrite direction, rmdir_precondition_after_mkdir repeat-split overshoot. * posix_errors.v: classic returns Prop but goal is sumbool — switch to excluded_middle_informative; safe_rmdir <-> rmdir_precondition needs explicit split (not repeat-split, since is_empty_dir nests). * rmo_operations.v: omega → lia (Coq 8.14+); multi_pass helpers reordered before obliterate_removes_path so the proof can use them; rewrite multi_pass_preserves_tree/mapping in obliterate_preserves_other_*. * extraction.v: missing Require Import String (Coq doesn't auto-import on Extract Inductive); proofs/coq/.gitignore adds extracted/. Three admits added (each model-gap, not laziness): * filesystem_composition.v:611 Example mkdir_two_dirs_reversible — non-LIFO sequence-reversal order; not derivable from two_op_sequence_reversible as stated. * rmo_operations.v:420 overwrite_pass_equalizes_storage — geometry hypothesis missing block_overwritten constraint; conclusion provably false for inputs with mismatched overwrite counters. * rmo_operations.v:531 obliterate_not_injective — depends on the admitted overwrite_pass_equalizes_storage. (Pre-existing: filesystem_composition.v:238 single_op_reversible from PR #48, OpRmdir/OpDeleteFile model gap.) CI re-enablement: * .github/workflows/validation.yml verify-proofs job: drop `if: false`, install coq via apt-get (37s vs ~5min for coqorg image per ephapax CI build oracle memory), build with coq_makefile + make -j1, then Print Assumptions for single_op_reversible, operation_sequence_reversible, reversible_creates_CNO, copy_file_reversible to surface the registered model gaps in the build log. * proofs/coq/_CoqProject: remove "currently disabled" note, bump last-verified to Coq 8.18.0 2026-06-01. Build verified locally: 11/11 .vo files green from clean state. * ci(verify-proofs): mkdir extracted/ before make (gitignored output dir) extraction.v's [Extraction "extracted/filesystem.ml" ...] writes into the gitignored directory, which doesn't exist on a fresh CI clone. Add a one- line mkdir before the make step. Local fresh clone reproduces the issue without this fix.
1 parent b36bcf0 commit 18e3e3c

10 files changed

Lines changed: 235 additions & 212 deletions

.github/workflows/validation.yml

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,25 +46,41 @@ jobs:
4646
path: validation-report.md
4747

4848
verify-proofs:
49+
name: verify-proofs (Coq build oracle)
4950
runs-on: ubuntu-latest
50-
if: false # Disabled until proof systems installed in CI
5151
steps:
5252
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4
5353

54-
- name: Install proof assistants
54+
- name: Install Coq via apt
5555
run: |
56-
# TODO: Install Coq, Lean 4, Agda, Isabelle, Z3
57-
echo "Proof verification disabled - install proof systems"
56+
sudo apt-get update
57+
sudo apt-get install -y coq
5858
59-
- name: Verify Lean 4 proofs
59+
- name: Show Coq version
60+
run: coqc --version
61+
62+
- name: Build Coq proofs (oracle)
63+
working-directory: proofs/coq
6064
run: |
61-
cd proofs/lean4
62-
lake build
65+
# extraction.v writes to extracted/filesystem.ml; the dir is gitignored, so create it.
66+
mkdir -p extracted
67+
coq_makefile -f _CoqProject -o Makefile
68+
make -j1
6369
64-
- name: Verify Coq proofs
70+
- name: Print Assumptions for top-level theorems
71+
working-directory: proofs/coq
6572
run: |
66-
cd proofs/coq
67-
make clean && make all
73+
# Coq disallows '-' in identifiers — use underscores in the helper file name.
74+
cat > /tmp/print_assumptions.v <<'EOF'
75+
Require Import ValenceShell.filesystem_composition.
76+
Require Import ValenceShell.copy_move_operations.
77+
Print Assumptions single_op_reversible.
78+
Print Assumptions operation_sequence_reversible.
79+
Print Assumptions reversible_creates_CNO.
80+
Print Assumptions copy_file_reversible.
81+
EOF
82+
coqc -R . ValenceShell /tmp/print_assumptions.v 2>&1 | tee print_assumptions.log
83+
echo "=== Print Assumptions surfaced (see job log above) ==="
6884
6985
property-testing:
7086
runs-on: ubuntu-latest

proofs/coq/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,6 @@ deps/
3131
Thumbs.db
3232
build/
3333
dist/
34+
35+
# Extracted OCaml code (output of extraction.v)
36+
extracted/

proofs/coq/_CoqProject

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
# Valence Shell — Coq Proof Project
33
#
44
# Minimum supported Coq version: 8.18.0
5-
# Last verified working: Coq 8.20.1 (2026-03-10)
6-
# CI workflow: .github/workflows/validation.yml (verify-proofs job, currently disabled)
5+
# Last verified working: Coq 8.18.0 (2026-06-01)
6+
# CI workflow: .github/workflows/validation.yml (verify-proofs job is ENABLED)
77
#
88
# Build: coq_makefile -f _CoqProject -o Makefile && make
99
# No external dependencies (no opam packages beyond stdlib).

proofs/coq/copy_move_operations.v

Lines changed: 59 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ Definition copy_file (src dst : Path) (fs : Filesystem) : Filesystem :=
4848

4949
(** * Move Operation *)
5050

51+
(** Helper: check if path is prefix of another *)
52+
Definition is_prefix (p1 p2 : Path) : Prop :=
53+
exists suffix, p2 = p1 ++ suffix.
54+
5155
(** Precondition for move/rename *)
5256
Definition move_precondition (src dst : Path) (fs : Filesystem) : Prop :=
5357
(* Source must exist *)
@@ -65,10 +69,6 @@ Definition move_precondition (src dst : Path) (fs : Filesystem) : Prop :=
6569
(* Must have write permission on destination parent *)
6670
has_write_permission (parent_path dst) fs.
6771

68-
(** Helper: check if path is prefix of another *)
69-
Definition is_prefix (p1 p2 : Path) : Prop :=
70-
exists suffix, p2 = p1 ++ suffix.
71-
7272
(** Move operation *)
7373
Definition move (src dst : Path) (fs : Filesystem) : Filesystem :=
7474
match fs src with
@@ -107,7 +107,11 @@ Proof.
107107
destruct Hpre as [[perms Hfile] _].
108108
rewrite Hfile.
109109
unfold fs_update.
110-
destruct (list_eq_dec string_dec dst src); [contradiction | reflexivity].
110+
destruct (list_eq_dec String.string_dec dst src) as [Heq | Hneq_dst].
111+
- (* dst = src — contradicts Hneq : src <> dst *)
112+
symmetry in Heq. contradiction.
113+
- (* dst <> src — both sides equal fs src *)
114+
symmetry. exact Hfile.
111115
Qed.
112116

113117
(** Theorem: copy creates exact duplicate of content *)
@@ -159,15 +163,15 @@ Theorem move_creates_destination :
159163
move_precondition src dst fs ->
160164
path_exists dst (move src dst fs).
161165
Proof.
162-
intros src dst fs [[node Hsrc] [Hnotdst _]].
166+
intros src dst fs [[node Hsrc] [Hnotdst [_ [Hneq _]]]].
163167
unfold path_exists, move.
164168
rewrite Hsrc.
165169
exists node.
166170
unfold fs_update.
167-
destruct (list_eq_dec string_dec src dst).
168-
- (* src = dst contradicts precondition *)
169-
destruct (list_eq_dec string_dec dst dst); [reflexivity | contradiction].
170-
- destruct (list_eq_dec string_dec dst dst); [reflexivity | contradiction].
171+
destruct (list_eq_dec String.string_dec src dst) as [Heq | _].
172+
- (* src = dst contradicts precondition Hneq *)
173+
contradiction.
174+
- destruct (list_eq_dec String.string_dec dst dst) as [_ | Hne]; [reflexivity | contradiction].
171175
Qed.
172176

173177
(** Theorem: move removes source *)
@@ -190,15 +194,14 @@ Theorem move_preserves_content :
190194
move_precondition src dst fs ->
191195
fs src = (move src dst fs) dst.
192196
Proof.
193-
intros src dst fs [[node Hsrc] _].
197+
intros src dst fs [[node Hsrc] [_ [_ [Hneq _]]]].
194198
unfold move.
195199
rewrite Hsrc.
196200
unfold fs_update.
197-
destruct (list_eq_dec string_dec src dst).
198-
- (* src = dst - contradiction *)
199-
subst.
200-
destruct (list_eq_dec string_dec dst dst); [reflexivity | contradiction].
201-
- destruct (list_eq_dec string_dec dst dst); [reflexivity | contradiction].
201+
destruct (list_eq_dec String.string_dec src dst) as [Heq | _].
202+
- (* src = dst contradicts Hneq *)
203+
contradiction.
204+
- destruct (list_eq_dec String.string_dec dst dst) as [_ | Hne]; [reflexivity | contradiction].
202205
Qed.
203206

204207
(** Theorem: move is reversible *)
@@ -208,37 +211,38 @@ Theorem move_reversible :
208211
move dst src (move src dst fs) = fs.
209212
Proof.
210213
intros src dst fs Hpre.
211-
unfold move.
212214
destruct Hpre as [[node Hsrc] [Hnotdst [_ [Hneq _]]]].
213-
rewrite Hsrc.
214-
unfold fs_update at 2.
215-
destruct (list_eq_dec string_dec src dst).
216-
- (* src = dst contradicts Hneq *)
217-
contradiction.
218-
- unfold fs_update at 1.
219-
destruct (list_eq_dec string_dec dst dst); [|contradiction].
220-
unfold fs_update.
221-
apply functional_extensionality.
222-
intros p.
223-
destruct (list_eq_dec string_dec dst p).
224-
+ (* dst = p: after reverse move, dst is cleared (None) = fs dst (also None) *)
215+
(* First compute the inner move: (move src dst fs) = fs_update src None (fs_update dst (Some node) fs) *)
216+
assert (Hinner : move src dst fs = fs_update src None (fs_update dst (Some node) fs)).
217+
{ unfold move. rewrite Hsrc. reflexivity. }
218+
rewrite Hinner.
219+
(* Now: move dst src (fs_update src None (fs_update dst (Some node) fs)) = fs *)
220+
(* Need to show (fs_update src None (fs_update dst (Some node) fs)) dst = Some node *)
221+
assert (Hdstnode : (fs_update src None (fs_update dst (Some node) fs)) dst = Some node).
222+
{ unfold fs_update.
223+
destruct (list_eq_dec String.string_dec src dst) as [Heq | _].
224+
- contradiction.
225+
- destruct (list_eq_dec String.string_dec dst dst) as [_ | Hne_dd]; [reflexivity | contradiction]. }
226+
unfold move at 1.
227+
rewrite Hdstnode.
228+
apply functional_extensionality.
229+
intros p.
230+
unfold fs_update.
231+
destruct (list_eq_dec String.string_dec dst p) as [Hdp | Hndp].
232+
- (* dst = p *)
233+
destruct (list_eq_dec String.string_dec src p) as [Hsp | Hnsp].
234+
+ (* src = p, but dst = p too, so src = dst — contradicts Hneq *)
235+
subst. contradiction.
236+
+ (* Goal: None = fs p. Use Hnotdst: dst=p so fs p must be None to avoid path_exists *)
237+
destruct (fs p) eqn:Hfsp.
238+
* exfalso. apply Hnotdst. exists f. rewrite Hdp. assumption.
239+
* reflexivity.
240+
- destruct (list_eq_dec String.string_dec src p) as [Hsp | Hnsp].
241+
+ (* src = p *)
225242
subst.
226-
destruct (list_eq_dec string_dec src p).
227-
* (* src = dst = p, contradiction *)
228-
contradiction.
229-
* (* source wasn't at dst — result at p is None *)
230-
unfold fs_update.
231-
destruct (list_eq_dec string_dec dst p); [|contradiction].
232-
(* Goal: None = fs p, derive from ¬ path_exists p fs *)
233-
destruct (fs p) eqn:Hfsp.
234-
-- exfalso. apply Hnotdst. exists f. assumption.
235-
-- reflexivity.
236-
+ destruct (list_eq_dec string_dec src p).
237-
* (* src = p *)
238-
subst.
239-
assumption.
240-
* (* neither src nor dst *)
241-
destruct (list_eq_dec string_dec dst p); [contradiction|reflexivity].
243+
symmetry. assumption.
244+
+ (* neither src nor dst at p *)
245+
reflexivity.
242246
Qed.
243247

244248
(** * Preservation Theorems *)
@@ -253,7 +257,9 @@ Proof.
253257
unfold copy_file.
254258
destruct (fs src).
255259
- unfold fs_update.
256-
destruct (list_eq_dec string_dec dst p); [contradiction | reflexivity].
260+
destruct (list_eq_dec String.string_dec dst p) as [Heq | _].
261+
+ symmetry in Heq. contradiction.
262+
+ reflexivity.
257263
- reflexivity.
258264
Qed.
259265

@@ -268,8 +274,11 @@ Proof.
268274
unfold move.
269275
destruct (fs src).
270276
- unfold fs_update.
271-
destruct (list_eq_dec string_dec src p); [contradiction|].
272-
destruct (list_eq_dec string_dec dst p); [contradiction|reflexivity].
277+
destruct (list_eq_dec String.string_dec src p) as [Heq | _].
278+
+ symmetry in Heq. contradiction.
279+
+ destruct (list_eq_dec String.string_dec dst p) as [Heq | _].
280+
* symmetry in Heq. contradiction.
281+
* reflexivity.
273282
- reflexivity.
274283
Qed.
275284

@@ -283,9 +292,8 @@ Theorem copy_then_move :
283292
(move dst dst2 (copy_file src dst fs)) dst2 = fs src.
284293
Proof.
285294
intros src dst dst2 fs Hcopy Hmove.
286-
rewrite <- copy_file_same_content with (src := src) (dst := dst) (fs := fs).
287-
- apply move_preserves_content. assumption.
288-
- assumption.
295+
rewrite <- (move_preserves_content dst dst2 (copy_file src dst fs) Hmove).
296+
symmetry. apply copy_file_same_content. assumption.
289297
Qed.
290298

291299
(** * Summary of Proven Claims *)

proofs/coq/extraction.v

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
This bridges the gap between formal proofs and real implementation.
55
*)
66

7+
Require Import String.
8+
Require Import List.
79
Require Import ExtrOcamlBasic.
810
Require Import ExtrOcamlString.
911

proofs/coq/file_content_operations.v

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -130,16 +130,11 @@ Proof.
130130
unfold read_file in Hold.
131131
destruct (fs p) as [n|] eqn:Hfs; [|discriminate].
132132
destruct (node_type n) eqn:Hntype; [|discriminate].
133-
rewrite Hntype in Hold. simpl in Hold.
133+
simpl in Hold.
134134
destruct (node_content n) as [c|] eqn:Hcontent; [|discriminate].
135135
injection Hold as Heq. subst c.
136-
(* Both (list_eq_dec p p) branches are trivially left/right *)
137-
destruct (list_eq_dec String.string_dec p p) as [_|Hcontra]; [|exact (Hcontra eq_refl)].
138-
rewrite Hfs. rewrite Hntype.
139-
(* After inner write: result is Some {|...; node_content := Some new_content|} *)
140-
destruct (list_eq_dec String.string_dec p p) as [_|Hcontra]; [|exact (Hcontra eq_refl)].
141-
rewrite Hfs. rewrite Hntype. simpl.
142-
(* node_type of the intermediate node is File, so outer write applies old_content *)
136+
destruct (list_eq_dec String.string_dec p p) as [_|Hcontra]; [|destruct (Hcontra eq_refl)].
137+
destruct (list_eq_dec String.string_dec p p) as [_|Hcontra]; [|destruct (Hcontra eq_refl)].
143138
destruct n as [nt perms cont]; simpl in *.
144139
subst nt. subst cont.
145140
reflexivity.
@@ -180,7 +175,7 @@ Proof.
180175
unfold write_file.
181176
destruct (list_eq_dec String.string_dec p p) as [_|Hcontra].
182177
+ destruct (fs p) as [node|] eqn:Hfs.
183-
* simpl. destruct (node_type node); reflexivity.
178+
* destruct (node_type node) eqn:Hnt; cbn; try rewrite Hnt; reflexivity.
184179
* simpl. reflexivity.
185180
+ contradiction.
186181
- reflexivity.
@@ -271,15 +266,12 @@ Proof.
271266
subst p'.
272267
unfold read_file in Hread.
273268
destruct Hpre as [node [Hnode [Htype [perms [Hperms Hwrite]]]]].
274-
rewrite Hnode in Hread.
275-
rewrite Htype in Hread.
276-
simpl in Hread.
277-
injection Hread as Heq.
269+
assert (Hc : node_content node = Some content).
270+
{ rewrite Hnode in Hread. cbn in Hread.
271+
rewrite Htype in Hread. exact Hread. }
278272
rewrite Hnode.
279-
rewrite Htype.
280-
destruct node; simpl in *.
281-
subst node_type0.
282-
subst node_content0.
273+
destruct node as [nt nperms ncont]; simpl in *.
274+
subst nt. subst ncont.
283275
reflexivity.
284276
+ (* p <> p' *)
285277
reflexivity.

0 commit comments

Comments
 (0)