Skip to content

Commit b369577

Browse files
hyperpolymathclaude
andcommitted
fix(proofs/idris2): redesign auditTrailCompleteness as a per-insertion theorem (closes #131)
Closes the third non-theorem from the #119 Category A inventory. Mirrors the #60 / #61 / #119A precedent: rather than discharging a provably-false claim with `believe_me`, restate it as a structurally honest one. ## auditTrailCompleteness (RMO.idr) Was: auditTrailCompleteness : (entries : List AuditEntry) -> (p : Path) -> (obliterated : ObliterationProof p) -> Elem p (map AuditEntry.path entries) Refutable by `entries := []`: an `ObliterationProof` for any `p` exists (just `MkObliterationProof p Clear 0 "unlink"`), yet the empty log contains no paths. The signature said "any obliteration is in any log" — plainly false. Now: auditTrailCompleteness : (log : List AuditEntry) -> (entry : AuditEntry) -> (p : Path) -> (insertedPath : AuditEntry.path entry = p) -> Elem p (map AuditEntry.path (appendAuditEntry log entry)) auditTrailCompleteness log entry p insertedPath = rewrite sym insertedPath in elemMap AuditEntry.path (elemAppRightSelf log entry) The corrected shape encodes the actual invariant: the path is present in the post-append log *because* the entry recording it was appended via the official `appendAuditEntry` constructor. The `insertedPath` premise witnesses the caller's commitment that the new entry indeed names the path we claim is logged. ## Closure path (zero new axioms) 1. `appendAuditEntry log entry` reduces to `log ++ [entry]` by definition (already proved as `appendOnlyAuditLog : Refl`). 2. `Data.List.Elem.elemMap` lifts list membership through `map`, giving `Elem (AuditEntry.path entry) (map AuditEntry.path (log ++ [entry]))`. 3. A short local helper `elemAppRightSelf` proves `Elem x (xs ++ [x])` by induction on the prefix — the lemma is not in Idris2 0.8.0 base. 4. The `insertedPath` equality rewrites `AuditEntry.path entry` to `p` in the goal. ## Verification Local Idris2 0.8.0: $ cd proofs/idris2 && rm -rf build && idris2 --build valence-shell.ipkg 1/4: Building Filesystem.Model OK 2/4: Building Filesystem.RMO OK 3/4: Building Filesystem.Operations OK 4/4: Building Filesystem.Composition OK $ grep -c '?auditTrailCompleteness' proofs/idris2/src/Filesystem/RMO.idr 0 $ grep -c 'believe_me' proofs/idris2/src/Filesystem/RMO.idr 2 # both inside ||| doc comments; no invocations No regressions. `--total` honoured. One hole eliminated; tree drops from 16 to 15. PROOF-NEEDS.md inventory + closure-history table updated to reflect the new state. Closes #131. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent a10005d commit b369577

2 files changed

Lines changed: 68 additions & 15 deletions

File tree

PROOF-NEEDS.md

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@
3838
| `Operations.idr` | 7 (`mkdirRmdirReversibleProof`, `rmdirMkdirReversibleProof`, `touchRmReversibleProof`, `rmTouchReversibleProof`, `writeFileReversibleProof`, `operationIndependenceProof`, `cnoWriteSameContentProof`) | 4 (`?rmdirPrfAfterMkdir`, `?mkdirPrfAfterRmdir`, `?rmPrfAfterTouch`, `?touchPrfAfterRm`) |
3939
| `Composition.idr` | 4 (`sequenceReversibleProof`, `compositionReversibleProof`, `undoRedoIdentityProof`, `undoRedoCompositionProof`) | 0 |
4040
| `Model.idr` | 2 (`equivReflProof`, `equivTransProof`; `equivSymProof` is closed via `andCommutative`) | 0 |
41-
| `RMO.idr` | 3 (`overwriteIrreversibleProof`, `hardwareEraseIrreversibleProof`, `auditTrailCompletenessProof`; `appendOnlyAuditLogProof` is closed via `Refl`) | 0 |
41+
| `RMO.idr` | 2 (`overwriteIrreversibleProof`, `hardwareEraseIrreversibleProof`; `appendOnlyAuditLogProof` is closed via `Refl`; `auditTrailCompletenessProof` is closed via redesign + `elemMap` + `elemAppRightSelf` — see #131) | 0 |
4242

43-
Drift from previous PROOF-NEEDS.md tally (22 holes) to current (16 holes) is mechanical: `equivSymProof` and `appendOnlyAuditLogProof` closed silently during the 2026-06-02 morning sweep (visible by grep but the inventory text was not updated). No body changes — this paragraph reconciles the count.
43+
Drift from previous PROOF-NEEDS.md tally (22 holes) to current (15 holes) is mechanical: `equivSymProof` and `appendOnlyAuditLogProof` closed silently during the 2026-06-02 morning sweep (visible by grep but the inventory text was not updated), and `auditTrailCompletenessProof` closed via signature redesign on 2026-06-03 (#131). No structural changes to the live source beyond #131 — this paragraph reconciles the count.
4444

4545
All `partial` markers in `proofs/idris2/src/Filesystem/*.idr` were cleared 2026-06-02 (PRs #108 + #109, closing #89). The total `partial` count is zero.
4646

@@ -61,6 +61,7 @@ All `partial` markers in `proofs/idris2/src/Filesystem/*.idr` were cleared 2026-
6161
| 2026-06-02 | Idris2 build oracle | `idris-verification.yml` workflow + Justfile recipes shipped (PR #106, closes #70) |
6262
| 2026-06-02 | Idris2 0.8.0 parse fixes | `AuditEntry.proof` keyword-clash rename (PR #112); `hardwareEraseIrreversible` multi-line signature fix (PR #113); `reverseConcat` closed via `Data.List.revAppend` (PR #115) |
6363
| 2026-06-02 PM | Coq admit triumvirate | `mkdir_two_dirs_reversible` restated to LIFO and closed (#56); `overwrite_pass_equalizes_storage` strengthened with `block_overwritten` constraint, closed with zero new axioms (#57); `obliterate_not_injective` threaded through the strengthened lemma + `multi_pass_same_start_same_result`, closed with only standard funext (#58). Coq layer now has **zero `Admitted` markers** (only the justified `Axiom is_empty_dir_dec` remains). |
64+
| 2026-06-03 | Idris2 RMO `auditTrailCompleteness` redesign | Signature redesigned away from the `entries = []`-refutable shape into a per-insertion claim threading `(log, entry, p, insertedPath : path entry = p)`; closed via `elemMap` (stdlib) + a local `elemAppRightSelf` induction + `sym insertedPath` rewrite (#131, mirrors the #60 / #61 / #119A precedent). Zero new axioms; zero `believe_me`. |
6465

6566
### What Needs Proving (current, prioritised by tractability × value)
6667

@@ -94,13 +95,17 @@ about `(q == p)` on opaque `Path` values inside `elem`, which Idris2
9495
`HardwareEraseProof -> (Unit -> Filesystem) -> Void` is refuted by
9596
any non-empty `recovery` (the function exists trivially). Correct
9697
shape needs the recovery to take the post-erase state as input.
97-
- `auditTrailCompletenessProof` (`RMO.idr:270`): conclusion
98-
`Elem p (map AuditEntry.path entries)` is refuted by `entries = []`.
99-
Correct shape needs an "entry was appended" precondition naming the
100-
insertion event in the log.
101-
102-
These three should be filed as **`#119` sub-issues** with the
103-
non-theorem refutations, in line with the #60 / #61 precedent.
98+
- ~~`auditTrailCompletenessProof` (`RMO.idr:270`)~~ **CLOSED via #131**:
99+
the previous shape `Elem p (map AuditEntry.path entries)` was refuted
100+
by `entries = []`. Redesigned to thread a single `entry`, an
101+
`insertedPath : AuditEntry.path entry = p` premise, and reason about
102+
`appendAuditEntry log entry = log ++ [entry]`. Closure via
103+
`elemMap` (stdlib) + `elemAppRightSelf` (local helper) + a
104+
`sym insertedPath` rewrite. Zero new axioms, zero `believe_me`.
105+
106+
The remaining two should be filed/handled as **`#119` sub-issues** with
107+
the non-theorem refutations, in line with the #60 / #61 / #131
108+
precedent.
104109

105110
**4 Operations.idr sub-holes** (`?rmdirPrfAfterMkdir`,
106111
`?mkdirPrfAfterRmdir`, `?rmPrfAfterTouch`, `?touchPrfAfterRm`) — same

proofs/idris2/src/Filesystem/RMO.idr

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -259,12 +259,60 @@ appendOnlyAuditLog :
259259
appendAuditEntry log entry = log ++ [entry]
260260
appendOnlyAuditLog log entry = Refl
261261

262-
||| Audit log provides complete history of obliterations
262+
||| `Elem` is preserved by right-appending the element itself.
263+
|||
264+
||| Pure list lemma — the appended item always lives at the end of
265+
||| the new list. Proof by straightforward induction on the prefix.
266+
|||
267+
||| Idris2 0.8.0 base does not ship this lemma directly; the closest
268+
||| stdlib helper is `Data.List.Elem.elemMap` (membership lifted
269+
||| through `map`), which we use as the second step of the audit
270+
||| completeness proof below.
271+
elemAppRightSelf : (xs : List a) -> (x : a) -> Elem x (xs ++ [x])
272+
elemAppRightSelf [] _ = Here
273+
elemAppRightSelf (_ :: ys) x = There (elemAppRightSelf ys x)
274+
275+
||| Audit log completeness — every path inserted via the official
276+
||| append-only constructor appears in the resulting log.
277+
|||
278+
||| Replaces the previous non-theorem signature
279+
|||
280+
||| auditTrailCompleteness :
281+
||| (entries : List AuditEntry) ->
282+
||| (p : Path) ->
283+
||| (obliterated : ObliterationProof p) ->
284+
||| Elem p (map AuditEntry.path entries)
285+
|||
286+
||| which was provably false (refuted by `entries = []`: an
287+
||| `ObliterationProof` for any `p` exists by `MkObliterationProof`,
288+
||| yet the empty log contains no paths). See issue #131 for the
289+
||| design rationale (mirrors the #60 / #61 / #119A precedent:
290+
||| redesign non-theorem signatures rather than close them with
291+
||| `believe_me`).
292+
|||
293+
||| The corrected shape encodes the actual invariant: the path is
294+
||| present in the post-append log *because* it was appended via
295+
||| `appendAuditEntry`. The premise `insertedPath` witnesses the
296+
||| caller's commitment that the new entry indeed records the path
297+
||| we claim is logged.
298+
|||
299+
||| Closure path (zero new axioms):
300+
||| 1. `appendAuditEntry log entry` reduces to `log ++ [entry]` by
301+
||| the definition of `appendAuditEntry`.
302+
||| 2. `Data.List.Elem.elemMap` lifts list membership through `map`,
303+
||| giving `Elem (AuditEntry.path entry) (map AuditEntry.path
304+
||| (log ++ [entry]))`.
305+
||| 3. `elemAppRightSelf` discharges `Elem entry (log ++ [entry])`
306+
||| by induction.
307+
||| 4. The `insertedPath` equality rewrites `AuditEntry.path entry`
308+
||| to `p` in the goal.
263309
export
264310
auditTrailCompleteness :
265-
(entries : List AuditEntry) ->
311+
(log : List AuditEntry) ->
312+
(entry : AuditEntry) ->
266313
(p : Path) ->
267-
-- If p was obliterated, it's in the audit log
268-
(obliterated : ObliterationProof p) ->
269-
Elem p (map AuditEntry.path entries) -- p appears in the log
270-
auditTrailCompleteness entries p oblitProof = ?auditTrailCompletenessProof
314+
(insertedPath : AuditEntry.path entry = p) ->
315+
Elem p (map AuditEntry.path (appendAuditEntry log entry))
316+
auditTrailCompleteness log entry p insertedPath =
317+
rewrite sym insertedPath in
318+
elemMap AuditEntry.path (elemAppRightSelf log entry)

0 commit comments

Comments
 (0)