Skip to content

Open a file from a directory listing named by a relative path - #15

Merged
viict merged 3 commits into
masterfrom
fix/relative-paths
Aug 13, 2026
Merged

Open a file from a directory listing named by a relative path#15
viict merged 3 commits into
masterfrom
fix/relative-paths

Conversation

@viict

@viict viict commented Aug 13, 2026

Copy link
Copy Markdown
Owner

tread ../ww-marketing-website/ lists the directory, and then every entry of
it fails to open with link escapes the index root -- with no --index
anywhere in sight. tread / fails the same way. Three defects stack up to
produce it, and each is a commit.

1. A relative path with .. did not parse

plat::path folded . and .. and treated a .. with nothing above it as
an error, because that is how a link escaping the corpus is caught. But the
same fold is used to parse a path that is merely written relatively, and
../notes/a.md names a real file. It returned None, contains said no, and
every entry of the listing was judged outside a root it was inside.

A .. with nothing above it now depends on who is asking: a relative path
keeps it, a rooted path drops it (/a/../.. is /), and joining a link onto
a base still refuses -- that caller is the one the error was written for.

2. The project search walked into the working directory

corpus_root climbed from the path's parent with Path::parent, whose answer
for .. is "" -- the working directory, which is not an ancestor of
../elsewhere at all. Run from a project, tread ../thing/ found this
project's .git and rooted the listing's corpus at a project the listing was
not in. The climb now runs on the path resolved against the cwd, folded rather
than joined, since <cwd>/../thing still holds <cwd> as a component.

3. A filesystem root fell back to .

Path::new("/").parent() is None, and the fallback was PathBuf::from("."),
so tread / rooted its corpus at wherever the reader was standing. Nothing is
above a filesystem root: the directory holding it is itself.

Verification

Reproduced first, from the reported command, through a pty:

before   CLAUDE.local.md: link escapes the index root
         tests/:          link escapes the index root
after    opens the file; `tests/` opens as a listing

before   tread /   ->  boot/: link escapes the index root
after    tread /   ->  Enter on etc/ opens `/etc  ·  177 entries  ·  [1 back]`
  • 1310 unit + 90 integration tests passing, from 1307 + 90 on master.
  • clippy --all-targets -D warnings clean; all five cross-targets check.
  • New tests: the relative-parent listing end to end in
    src/source/dir/tests.rs (with the cwd deliberately a different project,
    which is what pins defect 2), and the .. rules per platform in
    src/plat/path_tests.rs.

walking_off_the_front_is_an_error_not_a_clamp pinned the old behaviour and is
rewritten rather than deleted: it now asserts that join still refuses an
escape, that a rooted path clamps, and that a relative one keeps its ...

Found while reading agent trajectories on another branch; this predates that
work and is independent of it.

viict added 3 commits August 13, 2026 13:35
Folding treated a `..` with nothing above it as an error, so `../notes/a.md`
did not parse at all -- and a path that will not parse is contained by
nothing. Opening a directory the way a person names a sibling, `tread
../notes/`, therefore refused every entry of the listing for escaping a root
those entries were plainly inside.

A `..` with nothing above it now depends on who is asking. A relative path
keeps it, because that is part of what the path names. A rooted one drops it,
because nothing is above the root and `/a/../..` is `/`. Joining a link onto
a base still refuses outright: walking off the front of the base is exactly
what a link leaving the corpus does, and that is the one caller the error was
written for.
The climb to a project marker started at the path's parent and walked up with
Path::parent, whose answer for `..` is the empty path -- the working
directory. So `tread ../thing/` looked for a marker in the directory it had
just left, found this project's own, and rooted the listing's corpus at a
project the listing was not in.

The walk now runs on the path resolved against the cwd, and folded rather
than merely joined: `<cwd>/../thing` still holds `<cwd>` as a component, so
climbing it arrives back where it started.
A path with no parent fell back to `.`, so `tread /` rooted its corpus at
wherever the reader happened to be standing and then refused every entry of
`/` for escaping it. Nothing is above a filesystem root: the directory
holding it is itself.
@viict
viict merged commit 2dd00e8 into master Aug 13, 2026
7 checks passed
@viict
viict deleted the fix/relative-paths branch August 13, 2026 16:30
@viict viict self-assigned this Aug 13, 2026
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