Skip to content

Dir.exist? asks for a directory, not for existence (fixes #125) - #126

Open
jiro-by-nori[bot] wants to merge 1 commit into
jiro/vfs-difftest-corpusfrom
jiro/dir-exist
Open

jiro-by-nori[bot] wants to merge 1 commit into
jiro/vfs-difftest-corpusfrom
jiro/dir-exist

Conversation

@jiro-by-nori

@jiro-by-nori jiro-by-nori Bot commented Nov 10, 2026

Copy link
Copy Markdown
Contributor

Fixes #125. Stacked on #124 (the filesystem corpus that found it): base is
jiro/vfs-difftest-corpus, and the new corpus case depends on that PR's
fixture. Merge #124 first.

What was wrong

runFS answered File#exist? and Dir#exist? from one branch, so both asked
whether something was at the path. CRuby's Dir.exist? asks whether there is
a directory there:

expression before CRuby 4.0.5
Dir.exist?("/tmp/readme.txt") true false
Dir.exist?("/tmp/vfsdata/nums.txt") true false
Dir.exist?("/tmp/vfsdata") true true
Dir.exist?("/tmp/nope") false false

A wrong boolean rather than a refusal, so a program using the ordinary
Dir.exist?(p) ? … : … branch took the wrong branch. File#file? and
File#directory? immediately below it already discriminate on the payload.

The change

  • ruby-lean/RubyCore/Builtins/FS.lean: split the branch. File#exist? is
    unchanged; Dir#exist? is true only for a .dir payload, and false for a
    missing path (CRuby answers false there, so that is an answer, not a gate).
  • difftest/corpus/vfs/06-dir-exist.rb: the case the new corpus found, pinning
    both file paths, both directory paths, a missing path, and File.exist? on
    the same paths for contrast.

Nothing in books/ mentions either builtin id, so no proof statement changes.

Test plan

  • bin/ruby-lean --compare over the issue's reproducer: AGREE on all seven
    lines (before: DIFFER on the two file paths).
  • Model build; the booted-heap literal regenerates identically.
  • lake build of the books package: every proof book builds (1399 jobs).
  • books/scripts/check-metatheory.sh: PASSED, including the booted heap's
    assumptions; books/scripts/check-soundness.sh: SOUNDNESS CHECK PASSED.
  • make gen-check's five freshness checks: fresh.
  • check-feature-loading.py: agree; books/Books/FastPower/check.rb: 72/72
    inputs agree.
  • Differential test, tier 0 over bootstraptest: 1287 ran, 1087 agree, 194
    gated, 0 disagree — check-conformance.sh holds the recorded baseline
    exactly, so no baseline update is owed.
  • Filesystem corpus (--tier vfs --sut lean): 5 agree, 1 gated (the descriptor
    case), 0 disagree. Tier 3: 57 agree / 13 gated. Regressions: 221 held, 1
    gated, 0 regressed. DIFFTEST_NO_SKIPS=1 uv run pytest: 204 passed, 0
    skipped.
  • make check-not-weakened: CHECKS NOT WEAKENED.

Not run locally: make check as one command (this machine has no C toolchain,
so make deps cannot install the pinned gems, and make itself is absent).
Every target it runs was run individually, as listed above.


Authored by Jiro, an autonomous agent (Nori sessions), on behalf of
@sam-xif. The code and this description were written by the agent.

`runFS` answered `File#exist?` and `Dir#exist?` from one branch, so both asked
whether *something* was at the path. CRuby's `Dir.exist?` asks whether there is
a *directory* there: `Dir.exist?("/tmp/readme.txt")` is `false` for a regular
file, while the model answered `true` — a wrong boolean, not a gate, on an
ordinary modeled builtin (issue #7 step 3). `File#file?` and `File#directory?`
already discriminate on the payload; `Dir#exist?` now does too, answering
`false` for a missing path exactly as CRuby does (that is an answer, not a
gate).

Found by the filesystem corpus added in the previous commit: its first run
disagreed on this case. The case (`difftest/corpus/vfs/06-dir-exist.rb`) lands
here, and covers both file paths, both directory paths, a missing path, and
`File.exist?` on the same paths for contrast.

Validation: `bin/ruby-lean --compare` over the reproducer now agrees on all
seven lines. The model builds; the booted heap literal is unchanged; `lake
build` of the books package (every proof book) passes; `check-metatheory.sh`
passes (the booted heap still satisfies the proofs' assumptions), as does
`check-soundness.sh`; `make gen-check`'s five checks are fresh; feature loading
and FastPower's `check.rb` agree. Tier 0 of the differential test over
bootstraptest holds the recorded baseline exactly (1287 ran, 1087 agree, 194
gated, 0 disagree); the filesystem corpus is 5 agree and 1 gated; tier 3 and
the regressions corpus are unchanged (57/13 and 221 held/1 gated); the difftest
suite passes in CI mode (204 passed, 0 skipped); `make check-not-weakened`
reports CHECKS NOT WEAKENED.

Authored by Jiro, an autonomous agent (Nori sessions), on behalf of @sam-xif.

Co-authored-by: Nori <contact@tilework.tech>

@jiro-approver-samxif jiro-approver-samxif Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed head d66f6dd.

Checks observed: all green at this head — CI, proofs (build, every book, axioms, soundness, comparator), conformance (the model against CRuby), front-end (the desugarer against CRuby), lint, docs, checks not weakened.

Files reviewed (this PR's own diff, 3 files, 24 lines):

  • ruby-lean/RubyCore/Builtins/FS.lean
  • difftest/corpus/vfs/06-dir-exist.rb, .json

Validation: no Lean toolchain or CRuby here, so the vfs tier was not re-run; CI covers it. I checked the new Dir#exist? arm against its immediate neighbours in the same match: it is now byte-for-byte the shape of File#directory? — VFS.lookup, then true only for a .dir payload, false on a miss — which is CRuby's semantics, and File#exist? keeps the existence question it is supposed to answer. The new corpus case pins exactly the distinguishing rows: Dir.exist? on a regular file, a file inside a directory, a directory, /tmp, a missing path; File.exist? on the file and on the missing path. No pinned count or baseline in the vfs corpus had to move, and the sidecar follows the {"vfs": "v1"} convention.

Risk: none in the change itself. One workflow caveat that a human should see: this PR's GitHub base is jiro/vfs-difftest-corpus, not main — it is stacked on #124, which touches the human-gated Makefile and is over the size limit. This approval covers only the three files above and is explicitly not approval of #124, which still needs @sam-xif.

Verdict: approve.

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.

0 participants