Skip to content

fix(test): model oracle test fails instead of skipping as a pass (P0-4) - #214

Merged
hyperpolymath merged 3 commits into
mainfrom
fix/oracle-no-skip-as-pass
Oct 2, 2026
Merged

hyperpolymath merged 3 commits into
mainfrom
fix/oracle-no-skip-as-pass

Conversation

@hyperpolymath

Copy link
Copy Markdown
Owner

ULTRAPLAN P0-4 — the Lean↔Rust model-oracle correspondence test no longer passes when the oracle is missing. Stacked on P0-3 (base chore/lean-4-34-1).

Defect

tests/model_oracle_correspondence.rs returned (reported ok) when no oracle binary was found. rust-cli.yml and compilation_tests.yml run cargo test without building the oracle, and lean-verification.yml ran cargo test before building it, so most green runs never compared anything. locate_oracle() also silently fell back to the default path when VSH_MODEL_ORACLE pointed at nothing.

Change

  • The test is #[ignore = "needs Lean model_oracle — build with just build-model-oracle, run with --ignored"]: shown as ignored, never as ok.
  • When run, a missing oracle panics. A set-but-invalid VSH_MODEL_ORACLE panics instead of falling back.
  • Asserts sequences_run > 0 and checked_probes > 0, and prints both.
  • lean-verification.yml and Justfile test-correspondence-model pass --ignored --nocapture.

Evidence

Run Result
cargo test rc=0 — 786 passed, 0 failed, 15 ignored (the oracle test moved from "passed" to "ignored")
oracle built on v4.34.1, --ignored --nocapture rc=0 — 1218 probes agreed across 196 non-empty sequences (of 200 generated)
negative control VSH_MODEL_ORACLE=/nonexistent … --ignored rc=101 — VSH_MODEL_ORACLE is set to "/nonexistent", which is not a file
default oracle binary moved away, --ignored rc=101 — Lean model oracle not found at …

cargo fmt --check and clippy are clean. CHANGELOG.adoc:48-55 (a historical entry) still says the test "skips cleanly", so a new changelog entry may be wanted.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VxcAoyMQe7CjQwCKL18Mm4

hyperpolymath and others added 2 commits October 2, 2026 13:06
- lean-toolchain: leanprover/lean4:v4.34.1
- PathTraversal: List.dropLast_append_of_ne_nil no longer takes the
  list as an explicit argument; pass only the non-empty proof. This is
  the only v4.34 regression.
- SymlinkOperations: trailing `/-- Summary -/` doc comment had no
  declaration to attach to (parse error on v4.12.0 too); make it a
  plain block comment. No proof changed.
- rust-cli.yml lean4 job: elan --default-toolchain v4.34.1.
- lakefile header: last verified v4.34.1 (2026-10-02), and list which
  libs build. FileContentOperations, RMOOperations, CopyMoveOperations
  and PermissionOperations fail on v4.12.0 as well (measured from a
  clean `git archive HEAD` build) and are not touched here.

Theorem count 111 -> 111, sorry 0 -> 0, no axioms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The differential correspondence test returned early ("SKIP") when the
Lean model_oracle binary was missing, so a plain `cargo test` reported
it as passed with zero probes checked. A VSH_MODEL_ORACLE pointing at a
non-file silently fell back to the default path.

- Mark the test #[ignore] with a reason; plain `cargo test` now shows
  it as ignored, not passed.
- When run (--ignored), a missing oracle panics; an invalid
  VSH_MODEL_ORACLE panics instead of falling back.
- Assert sequences_run > 0 alongside checked_probes > 0 and print both.
- lean-verification.yml and `just test-correspondence-model` pass
  --ignored.

Verified: cargo test 786 passed / 0 failed / 15 ignored; with the
oracle built on Lean v4.34.1, 1218 probes agreed across 196 non-empty
sequences (of 200); VSH_MODEL_ORACLE=/nonexistent and a missing
default oracle both fail with exit 101.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 57 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 57aaf64e-4d43-483d-b34e-1731a7682fce

📥 Commits

Reviewing files that changed from the base of the PR and between 53ae47f and 182a723.

📒 Files selected for processing (3)
  • .github/workflows/lean-verification.yml
  • Justfile
  • impl/rust-cli/tests/model_oracle_correspondence.rs
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Base automatically changed from chore/lean-4-34-1 to main October 2, 2026 12:13
@hyperpolymath
hyperpolymath merged commit 3440d69 into main Oct 2, 2026
50 of 58 checks passed
@hyperpolymath
hyperpolymath deleted the fix/oracle-no-skip-as-pass branch October 2, 2026 12:14
@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 118 issues detected

Severity Count
🔴 Critical 9
🟠 High 27
🟡 Medium 82

⚠️ Action Required: Critical security issues found!

View findings
[
  {
    "reason": "Job `triage` in label-triage.yml has no `timeout-minutes:` declaration. Default is 6 hours — a stuck codeload fetch or runner hang can burn budget. Add `timeout-minutes: 10` (or proportional).",
    "type": "missing_timeout_minutes",
    "file": ".github/workflows/label-triage.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium",
    "recipe_id": "recipe-add-workflow-timeout-minutes",
    "job": "triage"
  },
  {
    "reason": "Job `sync` in labels.yml has no `timeout-minutes:` declaration. Default is 6 hours — a stuck codeload fetch or runner hang can burn budget. Add `timeout-minutes: 10` (or proportional).",
    "type": "missing_timeout_minutes",
    "file": ".github/workflows/labels.yml",
    "action": "flag",
    "rule_module": "workflow_audit",
    "severity": "medium",
    "recipe_id": "recipe-add-workflow-timeout-minutes",
    "job": "sync"
  },
  {
    "line": 38,
    "reason": "job in .github/workflows/labels.yml references `secrets.*` but does not install `step-security/harden-runner` — review outbound-egress monitoring",
    "type": "RE001",
    "file": ".github/workflows/labels.yml",
    "action": "report",
    "rule_module": "research_extensions",
    "severity": "medium"
  },
  {
    "line": 44,
    "reason": "job in .github/workflows/push-email-notify.yml references `secrets.*` but does not install `step-security/harden-runner` — review outbound-egress monitoring",
    "type": "RE001",
    "file": ".github/workflows/push-email-notify.yml",
    "action": "report",
    "rule_module": "research_extensions",
    "severity": "medium"
  },
  {
    "line": 46,
    "reason": "job in .github/workflows/cflite_batch.yml references `secrets.*` but does not install `step-security/harden-runner` — review outbound-egress monitoring",
    "type": "RE001",
    "file": ".github/workflows/cflite_batch.yml",
    "action": "report",
    "rule_module": "research_extensions",
    "severity": "medium"
  },
  {
    "line": 45,
    "reason": "job in .github/workflows/cflite_pr.yml references `secrets.*` but does not install `step-security/harden-runner` — review outbound-egress monitoring",
    "type": "RE001",
    "file": ".github/workflows/cflite_pr.yml",
    "action": "report",
    "rule_module": "research_extensions",
    "severity": "medium"
  },
  {
    "line": 20,
    "reason": "job in .github/workflows/instant-sync.yml references `secrets.*` but does not install `step-security/harden-runner` — review outbound-egress monitoring",
    "type": "RE001",
    "file": ".github/workflows/instant-sync.yml",
    "action": "report",
    "rule_module": "research_extensions",
    "severity": "medium"
  },
  {
    "line": 83,
    "reason": "job in .github/workflows/hypatia-scan.yml references `secrets.*` but does not install `step-security/harden-runner` — review outbound-egress monitoring",
    "type": "RE001",
    "file": ".github/workflows/hypatia-scan.yml",
    "action": "report",
    "rule_module": "research_extensions",
    "severity": "medium"
  },
  {
    "line": 52,
    "reason": "job in .github/workflows/label-triage.yml references `secrets.*` but does not install `step-security/harden-runner` — review outbound-egress monitoring",
    "type": "RE001",
    "file": ".github/workflows/label-triage.yml",
    "action": "report",
    "rule_module": "research_extensions",
    "severity": "medium"
  },
  {
    "line": 88,
    "reason": "workflow .github/workflows/compilation_tests.yml:88 step `Run ignored tests (stress)` swallows non-zero exit via `continue-on-error: true` — failures will be masked",
    "type": "RE005",
    "file": ".github/workflows/compilation_tests.yml",
    "action": "report",
    "rule_module": "research_extensions",
    "severity": "medium"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

hyperpolymath added a commit that referenced this pull request Oct 2, 2026
#215)

ULTRAPLAN **P0-5**. Until now the seven `verify_*` functions in
`impl/rust-cli/src/verification.rs` returned `Ok(())`. Each one now
checks, against the real filesystem, the Lean precondition its operation
is proved under.

| Rust | Lean | Checks, in order |
|---|---|---|
| `verify_mkdir` | `MkdirPrecondition` (`FilesystemModel.lean:119`) |
target absent (lstat) → parent exists → parent is dir → parent writable
|
| `verify_rmdir` | `RmdirPrecondition` (`FilesystemModel.lean:129`) |
exists (lstat) → real dir → no entries → not sandbox root → parent
writable |
| `verify_create_file` | `CreateFilePrecondition`
(`FileOperations.lean:11`) | as mkdir |
| `verify_delete_file` | `DeleteFilePrecondition`
(`FileOperations.lean:20`) | exists (lstat) → not dir → regular file or
symlink → parent writable |
| `verify_copy_file` | `copyFilePrecondition` | src exists → src not dir
→ dst absent → dst parent exists → src regular file → dst parent dir →
src readable → dst parent writable |
| `verify_move` | `movePrecondition` | src exists → dst absent → src ≠
dst → not into itself → dst parent exists → src/dst parents writable |
| `verify_symlink` | `SymlinkPrecondition` | link absent (lstat) →
parent exists → parent dir → parent writable (the target is deliberately
not checked) |

**Status, in the plan's taxonomy:** each mapping is **TESTED**, not
PROVEN. The Rust checks are written by hand to mirror the Lean `Prop`s,
not extracted from them. Correspondence is checked by review plus 41 new
positive and negative unit tests. The module doc says so.

## Commits
1. `fix(rust-cli): implement real Lean precondition checks`
- Path resolution moves to `state::resolve_under_root`, so the verifiers
and `ShellState::resolve_path` use one rule. The body is moved, not
changed.
- Each verifier first repeats the checks the command already makes, in
the same order and with byte-identical messages, then applies the
conditions only Lean has. User-visible errors therefore stay the same.
2. `chore(rust-cli): remove broken .cargo/config.toml and no-op
build.rs`
- The config's only content was rustflags with a literal `$REPOS_DIR`
link path, for a `lean-runtime-checks` feature that nothing defines.
There is no `extern "C"` and no `#[link]` in `src/`.
   - `docs/ABI-FFI-BOUNDARY.adoc` is updated to match.
3. `fix(rust-cli): keep rmdir read_dir error text identical to the
command`

## Behaviour changes (all from conditions the stub skipped)
- **Dangling symlink at a mkdir/touch/cp destination:** now *already
exists*. Before, mkdir and touch failed with an OS error, and cp wrote
*through* the link.
- **`rm` on a FIFO, socket or device:** refused (`Path is not a regular
file`). The model has no such node.
- New messages, reached only through the Lean-only conditions: `Parent
is not a directory (ENOTDIR)`, `… is not writable (EACCES)`, `Source is
not readable (EACCES)`, `Cannot remove the sandbox root`.
- `mv` follows Lean in not requiring the destination parent to be a
directory, so the rename itself reports that case.

## Evidence (rebased on main 3440d69)
```
cargo test                          suites=23 passed=827 failed=0 ignored=15
cargo clippy --all-targets -D warnings   clean
cargo fmt --check                   clean
```
- Before the change, on 4401867: 787 passed / 14 ignored.
- This PR adds 41 tests, all in `verification.rs`.
- One test moved from passed to ignored because #214 (P0-4) made the
oracle test `#[ignore]`.
- **Mutant:** putting the `Ok(())` stub back into `verify_mkdir` fails
all 5 mkdir negative tests, including the unwritable-parent test. That
shows the permission tests really run as uid 1000.
- The permission tests skip themselves when run as root.

## ⚠ Finding: `RmdirPrecondition` is unsatisfiable (PROVEN)
`isEmptyDir p fs` (`FilesystemModel.lean:69-71`) quantifies over
`child.isPrefixOf p`, which ranges over p's **ancestors**, not its
children. So it requires p's parent **not** to exist, while
`parentWritable` requires the parent to exist. This typechecks against
this repo's `FilesystemModel` on Lean v4.34.1:

```lean
theorem rmdir_precondition_vacuous (p : Path) (fs : Filesystem)
    (h : RmdirPrecondition p fs) : False
```
The file was checked with `lake env lean`. As a control, adding `example
: (1:Nat) = 2 := by decide` to the same file produces an error, so the
file is really checked.

**Consequence:** every theorem that takes `hpre : RmdirPrecondition p
fs` (in `FilesystemModel`, `FilesystemEquivalence`,
`FilesystemComposition`) holds vacuously, including the mkdir/rmdir
reversibility results.

The Rust here implements the evident *intent* ("the directory has no
entries"), and its docstring records the gap. The Lean fix
(`p.isPrefixOf child`, i.e. no descendant exists) belongs to the V1
proof phase, not this PR. It has been logged for the owner in
`dev-notes/inbox/findings.md`.

## Not in scope
- `ffi/rust/src/verification.rs` is a separate copy and is untouched.
- The `ShellState::root()` fallback to `/` for a non-UTF-8 root already
existed and is unchanged.
- The CLAUDE.md test-count table (736) is stale; this PR does not update
it.

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

https://claude.ai/code/session_01VxcAoyMQe7CjQwCKL18Mm4

---------

Co-authored-by: Claude Opus 5.5 <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