Skip to content

fix(proofs/idris2): redesign RMO theorems to theorem-shape-correct (closes #60 + #61) - #105

Merged
hyperpolymath merged 1 commit into
mainfrom
claude/issue-60-61-rmo-theorem-shape-redesign
Jun 2, 2026
Merged

fix(proofs/idris2): redesign RMO theorems to theorem-shape-correct (closes #60 + #61)#105
hyperpolymath merged 1 commit into
mainfrom
claude/issue-60-61-rmo-theorem-shape-redesign

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

Summary

Both secureDeleteIrreversible and gdprDeletionCompliant had non-theorem signatures (issues #60 + #61). This PR replaces them with structurally-correct theorems mirroring the Coq formalisations.

#60secureDeleteIrreversiblesecureDeleteNotInjective

Prior: recovery fs = fs -> Void is refuted by recovery = id.

New: mirrors Coq's rmo_operations.obliterate_not_injective. Two filesystems agreeing off-p collapse to the same post-deletion state. Closes via a one-line cong MkFS through a new removeEntryDeterminedByFilter lemma.

#61gdprDeletionCompliant redesign

Prior: recovery = id is refuted by recovery = const empty.

New: mirrors Coq's obliterate_leaves_no_trace shape. GDPR Article 17 compliance is a structural property of the deletion record (obliteration witness for the deleted path); closes by record projection.

Supporting changes (Model.idr)

  • keepIfNotP named predicate (anonymous lambdas don't unify across call sites in Idris2 0.8.0)
  • removeEntryDeterminedByFilter lemma (closed by cong MkFS)
  • Drive-by: DecEq PathNo absurdNo (\case Refl impossible) (Uninhabited instance unavailable in 0.8.0 base)
  • Drive-by: equivRefl (MkFS entries) = Refl (type error) → hole ?equivReflProof

The two drive-bys are required for idris2 --build to proceed past Model.idr into RMO.idr where the new theorems live. Pre-existing breakage at hardwareEraseIrreversible (unrelated () -> Filesystem parse issue) is out of scope.

Verification

Local Idris2 0.8.0:

$ idris2 --build proofs/idris2/valence-shell.ipkg
1/4: Building Filesystem.Model OK
2/4: Building Filesystem.RMO (passes new theorems, fails downstream
on pre-existing hardwareEraseIrreversible — #94 territory)

Both new theorems compile successfully. No Admitted / sorry / believe_me / assert_total / unsafeCoerce introduced; both close by direct construction.

Test plan

Docs updated

Closes#60.
Closes#61.

🤖 Generated with Claude Code

…ionCompliant to theorem-shape-correct (#60 + #61)
Both prior signatures were non-theorems:
* secureDeleteIrreversible: `recovery fs = fs -> Void` is refuted by
`recovery = id` (id always satisfies the premise, but cannot derive
Void).
* gdprDeletionCompliant: `recovery = id` is refuted by, e.g., `recovery
= const empty` (recovery is an arbitrary parameter; nothing forces it
to be id).
Replace with structurally-correct theorems mirroring the Coq
formalisations:
* `secureDeleteNotInjective` mirrors `rmo_operations.
obliterate_not_injective`: deletion is not injective on filesystem
state — two filesystems that agree off-`p` collapse to the same
post-deletion state. Closes via a new `removeEntryDeterminedByFilter`
lemma in `Filesystem.Model` (one-line `cong MkFS`).
* `gdprDeletionCompliant` mirrors `obliterate_leaves_no_trace`'s
structural shape: GDPR Article 17 compliance is a property of the
deletion record (witness obliteration for the deleted path), not
impossibility-of-recovery quantification. Closes via record
projection.
Supporting changes in `Filesystem/Model.idr`:
* Add `keepIfNotP` named predicate so `removeEntry` and proofs about
it refer to the same lambda (anonymous lambdas at separate call
sites don't unify in Idris2 0.8.0).
* Add `removeEntryDeterminedByFilter` lemma (closed by `cong MkFS`).
* Drive-by: fix `DecEq Path` `decEq Root (Cons _ _) = No absurd` to
use `No (\case Refl impossible)` (Uninhabited instance unavailable
in 0.8.0 base).
* Drive-by: change `equivRefl (MkFS entries) = Refl` (type error —
boolean RHS doesn't reduce to `True`) to a hole `?equivReflProof`,
consistent with the file's existing `equivSym`/`equivTrans` holes.
These drive-bys are required for `idris2 --build valence-shell.ipkg`
to proceed past Model.idr to RMO.idr (where it now successfully
typechecks the new theorems). Pre-existing breakage at
`hardwareEraseIrreversible` (unrelated `() -> Filesystem`-style
parse issue) is out of scope for #60/#61.
Verification (local, IDRIS2_PREFIX=$(idris2-install)):
$ idris2 --build proofs/idris2/valence-shell.ipkg
1/4: Building Filesystem.Model ✓
2/4: Building Filesystem.RMO (passes new theorems, then fails on
pre-existing hardwareEraseIrreversible — #94 territory)
Honest-admit policy: no Admitted / sorry / believe_me /
assert_total / unsafeCoerce introduced. Both new proofs close by
direct construction.
Docs updated:
* `PROOF-NEEDS.md` — #60/#61 closed, reference `?overwriteIrreversible`
as remaining open hole at this layer.
* `docs/PROOF-NARRATIVE.adoc` — table rows for lines 92 + 169 marked
CLOSED with cross-ref.
* `proofs/idris2/README.md` — theorem 15 + 17 entries updated;
irreversibility section rewritten with new signature.
* `CHANGELOG.adoc` — entry added under 2026-06-01 proof work.
Closes#60.
Closes#61.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 142 issues detected

SeverityCount
🔴 Critical10
🟠 High37
🟡 Medium95

⚠️Action Required: Critical security issues found!

View findings
[
{
"reason": "Workflow executes remote script directly (curl/wget piped to shell). Download, verify checksum/signature, then execute.",
"type": "download_then_run",
"file": "lean-verification.yml",
"action": "verify_download_integrity",
"rule_module": "workflow_audit",
"severity": "high"
},
{
"reason": "Workflow executes remote script directly (curl/wget piped to shell). Download, verify checksum/signature, then execute.",
"type": "download_then_run",
"file": "rust-cli.yml",
"action": "verify_download_integrity",
"rule_module": "workflow_audit",
"severity": "high"
},
{
"reason": "Issue in boj-build.yml",
"type": "missing_timeout_minutes",
"file": "boj-build.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in casket-pages.yml",
"type": "missing_timeout_minutes",
"file": "casket-pages.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in casket-pages.yml",
"type": "missing_timeout_minutes",
"file": "casket-pages.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in cflite_batch.yml",
"type": "missing_timeout_minutes",
"file": "cflite_batch.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in cflite_pr.yml",
"type": "missing_timeout_minutes",
"file": "cflite_pr.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in codeql.yml",
"type": "missing_timeout_minutes",
"file": "codeql.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in compilation_tests.yml",
"type": "missing_timeout_minutes",
"file": "compilation_tests.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in compilation_tests.yml",
"type": "missing_timeout_minutes",
"file": "compilation_tests.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
}
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

@hyperpolymath
hyperpolymath merged commit 8149667 into mainJun 2, 2026
25 of 30 checks passed
@hyperpolymath
hyperpolymath deleted the claude/issue-60-61-rmo-theorem-shape-redesign branch June 2, 2026 09:27
hyperpolymath added a commit that referenced this pull request Jun 2, 2026
* fix(licence): prepend SPDX-License-Identifier header to LICENSE
Adds the `SPDX-License-Identifier: MPL-2.0` first-line marker the
estate `check-licence-consistency.sh` governance script requires,
followed by a blank line preserving the existing MPL-2.0 text intact.
Clears `governance / Licence consistency` red inherited by 4 open PRs
(#105, #106, #107, #108).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(proofs): annotate 4 undocumented admits in rmo_operations.v with TRUSTED markers
The estate `check-trusted-base.sh` policy script walks back from each
`Admitted.` line through *consecutive comment lines* (lines whose first
non-whitespace char matches a comment marker `(*` / `*` / `--` / `//` /
`|||` / `#`) and accepts an annotation if a `TRUSTED:` or `AXIOM:` token
appears in that block.
Two prior issues:
1. **Lines 419 + 530** were false-positive markers: the regex
`^[[:space:]]*(Axiom|Admitted|admit\.)` matched the *prose* line
`Admitted pending follow-up. *)` inside a `(* ... *)` comment block
(because after stripping leading whitespace, the line begins with
the keyword `Admitted`). Rewording to `Stub pending follow-up. *)`
eliminates the false positives — now 12 markers total (was 14).
2. **Lines 420 + 531** (now 421 + 533) — the real `Admitted.` lines —
were preceded by `*)` closing-bracket lines that the walker does
NOT recognise as comment lines (no leading `*` / `(*` token), so
it never reached the inline `TRUSTED:` annotation buried deeper
in the comment block.
Fix: prepend a single-line `(* TRUSTED: <rationale> *)` comment
immediately before each `Admitted.`, citing the affected theorem
(`overwrite_pass_equalizes_storage`, `obliterate_not_injective`) and
referring downstream to the preceding model-gap explanation.
Clears `governance / Trusted-base reduction policy` red inherited by
4 open PRs (#105, #106, #107, #108). Underlying model gap (block_overwritten
counter mismatch) remains open as proof debt.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hyperpolymath added a commit that referenced this pull request Jun 2, 2026
…ecureDelete + gdprDelete (2 of 10 from #89) (#108)
Two of the 10 `partial` markers in #89's scope were misleading
annotations: `secureDelete` and `gdprDelete` are IO computations,
exhaust their case-splits, and never recurse. IO does not introduce
partiality in Idris2 — these are legitimately total.
Verified locally with #105's Model.idr fixes applied (post-#105 the
Idris2 build proceeds past both functions without partial-checker
complaint).
The remaining 8 partial markers all live in `Composition.idr`:
- `applyOp` / `applySequence` — partial because operations have
preconditions that aren't represented in the type. Requires a real
refactor to either return `Maybe Filesystem` or carry precondition
proofs as dependent arguments.
- `execute` / `undo` / `redo` — partial because they call `applyOp`;
cascade-cleanup as applyOp becomes total.
- `sequenceReversible` / `undoRedoIdentity` / `undoRedoComposition` —
proof bodies with `?holes`; require closure before totality.
Per #89's resolution: gap-per-PR. The Composition.idr refactor is
substantive enough for its own track and is gated on #70's build
oracle landing (in flight as #106) so each conversion can be machine-
verified.
Closes 2 of 10 markers in #89; #89 stays open for the remaining 8.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hyperpolymath added a commit that referenced this pull request Jun 2, 2026
… + Operations.idr type errors (8 of 10 from #89) (#109)
Closes the remaining 8 partial markers in `Composition.idr` (PR #108 was
the first 2 of 10).
## Composition.idr — 8 partial markers dropped
Root cause: `applyOp` called the precondition-requiring wrappers from
`Filesystem.Operations` (mkdir/rmdir/touch/rm/writeFile) which take
`{auto prf : MkdirPrecondition p fs}` etc. No precondition was available
at the call site, making `applyOp` partial. This cascaded to
`applySequence`, `execute`, `undo`, `redo`, and the proofs above them.
Fix: `applyOp` now calls the underlying `addEntry` / `removeEntry` /
`updateEntry` primitives from `Filesystem.Model` directly. The wrappers'
bodies are exactly these primitives, so the behaviour is unchanged.
Precondition-aware variants stay in `Filesystem.Operations` for
theorem-proving contexts; the composition-layer one is the runtime
sequence-application semantics.
With `applyOp` total, the cascade clears:
- `applyOp` / `applySequence` — direct
- `execute` / `undo` / `redo` — cascade (also promoted to `public export`
so their names are visible in subsequent theorem signatures)
- `sequenceReversible` / `undoRedoIdentity` / `undoRedoComposition` —
totality checker accepts the hole-bodied functions (Idris2 0.8.0 does
not reject `?holes` in `total` functions)
## Composition.idr — additional theorem-shape fixes
- `sequenceReversible`: `All (\op => isReversible op = True)` now
imports `Data.List.Quantifiers.All` (the Idris2-0.8.0 location;
`Data.List.All` is deprecated).
- `undoRedoIdentity`: replaced `fromJust $ undo X` (undefined in Idris2
0.8.0 stdlib and partial anyway) with the Maybe-monadic chain
`undo X >>= redo`.
- `undoRedoComposition`: replaced `length undoStack >= n` (a `Bool`,
not a `Type`) with `LTE n (length undoStack)`; reformulated the body
similarly with `>>=` composition.
- `reverseConcat`: body reduced to a hole. The original closed-form
proof relied on `reverse`'s Idris-1-style structural reduction; the
Idris2 0.8.0 tail-recursive `reverse` doesn't unfold the same way.
Needs a different proof strategy (likely `Data.List.reverseOntoSpec`);
parked as a sub-marker under #89.
## Operations.idr — drive-by type-signature fixes
Required to make Operations.idr typecheck so Composition.idr can import
it. Both were pre-existing type errors (not partiality):
- `writeFileReversible`: `old` was a free variable in an auto-implicit
context, creating an unsolvable existential at the call site. Made
`old` an explicit parameter and added the explicit `prfPreserved`
preservation auto-implicit (the inner `writeFile` requires a fresh
preservation proof not in scope at the outer level). The proof body
remains a `?hole` (theorem still to write).
- `operationIndependence`: `(p1 /= p2)` was a `Bool` term in a precondition
position that needs a `Type`. Replaced with `Not (p1 = p2)`. Also added
two more auto-implicit `MkdirPrecondition`s for the post-application
states (mkdir does not automatically preserve `MkdirPrecondition` for
sibling paths — needs a separate lemma).
## Verification
Locally built against #105's Model.idr fixes (RMO temporarily excluded
from ipkg modules list since RMO has additional pre-existing parse
issues at `hardwareEraseIrreversible` not in #89 scope):
```
$ idris2 --build proofs/idris2/valence-shell.ipkg
1/3: Building Filesystem.Model OK
2/3: Building Filesystem.Operations OK
3/3: Building Filesystem.Composition OK
```
No errors, no warnings. `--total` (set in ipkg) verifies all functions
(including the `?hole`-bodied ones) pass the totality checker.
## Closes 8 of 10 from #89
| File | Function | Before | After |
|---|---|---|---|
| Composition.idr | `applyOp` | partial (needs precondition) | TOTAL via Model primitives |
| Composition.idr | `applySequence` | partial (cascade) | TOTAL |
| Composition.idr | `execute` | partial (cascade) | TOTAL + public export |
| Composition.idr | `undo` | partial (cascade) | TOTAL + public export |
| Composition.idr | `redo` | partial (cascade) | TOTAL + public export |
| Composition.idr | `sequenceReversible` | partial (hole body) | TOTAL (hole accepted) |
| Composition.idr | `undoRedoIdentity` | partial (hole body + bad type) | TOTAL (type fixed, hole accepted) |
| Composition.idr | `undoRedoComposition` | partial (Bool/Type mismatch) | TOTAL (type fixed, hole accepted) |
Combined with PR #108 (`secureDelete` + `gdprDelete`), all 10 partial
markers in #89's scope are now resolved. Theorem bodies remain as
`?holes` — that's separate proof debt, not partiality.
#89 can close.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hyperpolymath added a commit that referenced this pull request Jun 2, 2026
After PRs #105 / #106 / #108 / #109 admin-merged closing #60 / #61 /
#70 / #89, several files held stale claims:
- PROOF-NEEDS.md: Idris2 listed "23 holes + 8 partial across 4 files"
pre-#108/#109 — partial count is now 0 (10 dropped: 2 in RMO from
IO-totality reclassification, 8 in Composition from primitive-call
refactor). Coq admit detail rewritten: the single_op_reversible
OpRmdir admit was Qed-closed by PR #67 (2026-06-01); 3 new admits
surfaced from PR #55's design-gap pass tracked as #56/#57/#58.
- proofs/idris2/README.md: 21 holes -> 23 holes; "Known oracle status
2026-06-01" -> 2026-06-02, with partial-count reference corrected.
- ROADMAP.adoc: last-refreshed bumped + post-merge attribution.
- CHANGELOG.adoc: new "Added -- 2026-06-02" section recording the
Idris2 cleanup wave + branch sweep + #111 salvage decision.
- .machine_readable/6a2/STATE.a2ml: last-updated 2026-04-19 -> 2026-06-02
(6-week drift); admitted 1 -> 3 with per-admit detail (#56/#57/#58);
session-history bullets added for 2026-06-01 + 2026-06-02.
- 0-AI-MANIFEST.a2ml: last-updated bumped; tests 736 -> 757 (PR #72);
proof-holes-remaining split into coq-admits + idris2 totals.
Issue #111 filed during the sweep for `.github/copilot/coding-agent.yml`
salvaged from the deleted `claude/safedom-res-stale-sweep` branch.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hyperpolymath added a commit that referenced this pull request Jun 2, 2026
…marker) (#115)
Closes the parked `?reverseConcatProof` hole from PR #109 (Composition.idr).
The Idris2 0.8.0 stdlib provides:
Data.List.revAppend :
(vs, ns : List a) -> reverse ns ++ reverse vs = reverse (vs ++ ns)
Which is exactly our claim, modulo direction of the equation. Applying
`sym` flips it to the conventional "concatenation distributes over
reverse" framing used by our local theorem:
reverseConcat xs ys : reverse (xs ++ ys) = reverse ys ++ reverse xs
reverseConcat xs ys = sym (revAppend xs ys)
The previous closed-form proof using induction + `rewrite` failed under
Idris2 0.8.0 because the stdlib's `reverse` is tail-recursive via
`reverseOnto`, and so does not structurally reduce per the Idris-1-style
expansion `reverse (x :: xs) = reverse xs ++ [x]`. Delegating to the
stdlib lemma is both shorter and more robust.
Verified locally with the full stacked context (#105 + #108 + #109 +
hardware-erase + audit-entry fixes + RMO overwriteIrreversible /
auditTrailCompleteness drive-bys for build progression). All 4 modules
build clean.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hyperpolymath added a commit that referenced this pull request Jun 2, 2026
The Idris2 build oracle was landed non-blocking in PR #106 because
several pre-existing typecheck/parse errors prevented `idris2 --build`
from succeeding. All four blockers have since cleared:
* #105 — Model.idr DecEq impossible-pattern + equivRefl hole
* #109 — Composition.idr applyOp via Model primitives + cascade
* #112 — RMO AuditEntry.proof rename (Idris2 0.8.0 reserved keyword)
* #113 — RMO hardwareEraseIrreversible single-line signature
* #117 — RMO overwriteIrreversible LTE + auditTrailCompleteness Elem import
Local verification post-#117:
$ idris2 --build proofs/idris2/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
Flip the workflow's terminal `exit 0` → `exit 1`, upgrade the warning
to an error, and document the precondition chain.
Follow-up (owner action): add `verify-idris2 (Idris2 build oracle)` to
the repo's branch protection required-checks list so the strict gate
propagates to merge blocking.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant

@hyperpolymath