fix(rust-cli): real Lean precondition checks in verification.rs (P0-5) - #215
Conversation
…n.rs The seven verify_* functions returned Ok(()) unconditionally while commands.rs called them as the Lean 4 precondition gate. Each now checks the real filesystem under the sandbox root against the Lean structure it mirrors: verify_mkdir MkdirPrecondition (FilesystemModel.lean) verify_rmdir RmdirPrecondition (FilesystemModel.lean) verify_create_file CreateFilePrecondition (FileOperations.lean) verify_delete_file DeleteFilePrecondition (FileOperations.lean) verify_copy_file copyFilePrecondition (CopyMoveOperations.lean) verify_move movePrecondition (CopyMoveOperations.lean) verify_symlink SymlinkPrecondition (SymlinkOperations.lean) Conditions the commands already enforced are checked first, in the command's order and with byte-identical messages, so user-visible error text is unchanged; Lean-only conditions (parentIsDir, parentWritable, hasReadPermission, rmdir notRoot, isFile excluding FIFOs/devices) follow. pathExists uses lstat, so a dangling symlink at a destination is now an EEXIST precondition failure rather than an OS error or a write through the link. Path resolution is extracted from ShellState::resolve_path into state::resolve_under_root so the checks resolve paths exactly as the commands do. Adds 41 unit tests (positive and negative per function, permission cases skipped when running as root). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VxcAoyMQe7CjQwCKL18Mm4
impl/rust-cli/.cargo/config.toml carried rustflags with a literal, unexpanded `/var$REPOS_DIR/valence-shell/impl/zig/zig-out/lib` link path for a `lean-runtime-checks` feature that does not exist in Cargo.toml. Nothing in the crate links the Zig library (no extern "C" or #[link] in src/), so the block only injected a bogus -L and rpath into every x86_64-linux build. It was the file's only content, so the file is deleted. build.rs was a documented no-op; Cargo.toml has no `build =` key, so deleting the file is sufficient. docs/ABI-FFI-BOUNDARY.adoc is updated to stop describing the no-op build.rs. cargo build, clippy --all-targets (0 warnings) and cargo test (828 passed, 0 failed, 14 ignored) re-run after the removal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VxcAoyMQe7CjQwCKL18Mm4
verify_rmdir wrapped read_dir in a context string, so an unreadable directory reported "rmdir: cannot read directory" instead of the OS error the rmdir command has always surfaced. Use bare `?` as the command does. Also document on verify_rmdir that Lean `isEmptyDir` (FilesystemModel.lean) quantifies over `child.isPrefixOf p` - the ancestors of p, not its descendants - which contradicts `parentWritable`, making RmdirPrecondition unsatisfiable as written for any non-root path. The Rust check implements the evident intent (no directory entries). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VxcAoyMQe7CjQwCKL18Mm4
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. Note Currently processing new changes in this PR. This may take a few minutes, please wait... ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Advanced Run ID: 📒 Files selected for processing (6)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
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. Comment |
|
Autopilot could not be updated. Open Coding to check access and billing. |
|
❌ Failed to create Coding Agent finishing-touch task. Please try again. |
🔍 Hypatia Security ScanFindings: 120 issues detected
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 |
ULTRAPLAN P0-5. Until now the seven
verify_*functions inimpl/rust-cli/src/verification.rsreturnedOk(()). Each one now checks, against the real filesystem, the Lean precondition its operation is proved under.verify_mkdirMkdirPrecondition(FilesystemModel.lean:119)verify_rmdirRmdirPrecondition(FilesystemModel.lean:129)verify_create_fileCreateFilePrecondition(FileOperations.lean:11)verify_delete_fileDeleteFilePrecondition(FileOperations.lean:20)verify_copy_filecopyFilePreconditionverify_movemovePreconditionverify_symlinkSymlinkPreconditionStatus, in the plan's taxonomy: each mapping is TESTED, not PROVEN. The Rust checks are written by hand to mirror the Lean
Props, not extracted from them. Correspondence is checked by review plus 41 new positive and negative unit tests. The module doc says so.Commits
fix(rust-cli): implement real Lean precondition checksstate::resolve_under_root, so the verifiers andShellState::resolve_pathuse one rule. The body is moved, not changed.chore(rust-cli): remove broken .cargo/config.toml and no-op build.rs$REPOS_DIRlink path, for alean-runtime-checksfeature that nothing defines. There is noextern "C"and no#[link]insrc/.docs/ABI-FFI-BOUNDARY.adocis updated to match.fix(rust-cli): keep rmdir read_dir error text identical to the commandBehaviour changes (all from conditions the stub skipped)
rmon a FIFO, socket or device: refused (Path is not a regular file). The model has no such node.Parent is not a directory (ENOTDIR),… is not writable (EACCES),Source is not readable (EACCES),Cannot remove the sandbox root.mvfollows Lean in not requiring the destination parent to be a directory, so the rename itself reports that case.Evidence (rebased on main 3440d69)
verification.rs.#[ignore].Ok(())stub back intoverify_mkdirfails all 5 mkdir negative tests, including the unwritable-parent test. That shows the permission tests really run as uid 1000.⚠ Finding:
RmdirPreconditionis unsatisfiable (PROVEN)isEmptyDir p fs(FilesystemModel.lean:69-71) quantifies overchild.isPrefixOf p, which ranges over p's ancestors, not its children. So it requires p's parent not to exist, whileparentWritablerequires the parent to exist. This typechecks against this repo'sFilesystemModelon Lean v4.34.1:The file was checked with
lake env lean. As a control, addingexample : (1:Nat) = 2 := by decideto the same file produces an error, so the file is really checked.Consequence: every theorem that takes
hpre : RmdirPrecondition p fs(inFilesystemModel,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 indev-notes/inbox/findings.md.Not in scope
ffi/rust/src/verification.rsis a separate copy and is untouched.ShellState::root()fallback to/for a non-UTF-8 root already existed and is unchanged.🤖 Generated with Claude Code
https://claude.ai/code/session_01VxcAoyMQe7CjQwCKL18Mm4