Skip to content

fix: a relative current_dir runs a relative program, and inspect's help says what it does - #561

Merged
vyncint merged 1 commit into
mainfrom
fix/relative-cwd-and-inspect-help
Oct 7, 2026
Merged

vyncint merged 1 commit into
mainfrom
fix/relative-cwd-and-inspect-help

Conversation

@vyncint

@vyncint vyncint commented Oct 6, 2026

Copy link
Copy Markdown
Owner

A relative TerminalBuilder::current_dir, and so termlens inspect --cwd ./examples ./myapp, never worked with a relative program path. The PTY
layer joined the directory onto the program, ./examples/./myapp, and the
child then entered the directory before exec, so the path was looked up a
second time from inside it and did not exist. The exec failure could not
even be reported: the PTY layer's pre-exec hook closes every inherited
descriptor, std's error pipe included, so the child died of a runtime
abort, fatal runtime error: assertion failed: output.write(&bytes) .is_ok(), which inspect showed as exited: killed by signal: Aborted.
0.11.4 behaves the same. spawn now makes a relative directory absolute
against the test process's own first, as std::process::Command treats
one, and a relative program then resolves inside it as documented.

Tests, each checked to fail without the fix:

  • process.rs spawns ./emit from a relative current_dir. The scratch
    directory sits deeper than the test's own working directory, because a
    directory as deep as it, like target/debug beside crates/termlens, maps
    onto itself when the path is applied twice and hid the bug in a first
    version of this test. It now asserts that before trusting a pass.
  • inspect.rs runs the example and termlens inspect with
    --cwd inspect-relative ./echo from the directory above.

The help of termlens inspect and examples/inspect.rs, which mirror each
other, now says what three behaviours are, all checked by running them:

  • the silence window starts only once the program has drawn something,
    so a program that only clears the screen runs to the deadline;
  • the child also gets TERM=xterm-256color and SHELL=/bin/sh, and
    --inherit-env keeps the caller's environment but for TERM;
  • --cwd's argument is DIR, not PATH beside the PATH variable, in the help
    and in its diagnostic (pinned in both test suites), and a relative
    program path is found inside it.

The CLI README's --cwd ./examples ./myapp example is the broken case;
it and its paragraph now say where the program is found, as does the
skill's line. Found by a documentation review.

…lp says what it does

A relative `TerminalBuilder::current_dir`, and so `termlens inspect --cwd
./examples ./myapp`, never worked with a relative program path. The PTY
layer joined the directory onto the program, `./examples/./myapp`, and the
child then entered the directory before exec, so the path was looked up a
second time from inside it and did not exist. The exec failure could not
even be reported: the PTY layer's pre-exec hook closes every inherited
descriptor, std's error pipe included, so the child died of a runtime
abort, `fatal runtime error: assertion failed: output.write(&bytes)
.is_ok()`, which inspect showed as `exited: killed by signal: Aborted`.
0.11.4 behaves the same. `spawn` now makes a relative directory absolute
against the test process's own first, as `std::process::Command` treats
one, and a relative program then resolves inside it as documented.

Tests, each checked to fail without the fix:
- process.rs spawns `./emit` from a relative `current_dir`. The scratch
  directory sits deeper than the test's own working directory, because a
  directory as deep as it, like target/debug beside crates/termlens, maps
  onto itself when the path is applied twice and hid the bug in a first
  version of this test. It now asserts that before trusting a pass.
- inspect.rs runs the example and `termlens inspect` with
  `--cwd inspect-relative ./echo` from the directory above.

The help of `termlens inspect` and examples/inspect.rs, which mirror each
other, now says what three behaviours are, all checked by running them:
- the silence window starts only once the program has drawn something,
  so a program that only clears the screen runs to the deadline;
- the child also gets TERM=xterm-256color and SHELL=/bin/sh, and
  --inherit-env keeps the caller's environment but for TERM;
- --cwd's argument is DIR, not PATH beside the PATH variable, in the help
  and in its diagnostic (pinned in both test suites), and a relative
  program path is found inside it.

The CLI README's `--cwd ./examples ./myapp` example is the broken case;
it and its paragraph now say where the program is found, as does the
skill's line. Found by a documentation review.

Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com>
vyncint added a commit that referenced this pull request Oct 7, 2026
Each was checked against the code or by running it before it changed;
one more item is a style tidy, marked as such.

README:
- `Screen::parse` reads the snapshot format, not an insta `.snap`: it
  rejects the `---` header on line 1. The body below it parses, and the
  `termlens` command reads a whole `.snap` because it strips the header.
- The skill has five recipes, not four.

SKILL.md:
- §7's mask row said `mask_matching` "spans a wrap the way `find_all`
  does". A value split by a soft wrap is two rows and stays unmasked
  (measured: 10 columns, `SECRET-VALUE-42`); a needle with `\n` is what
  spans rows. Rule 12 already says so for `contains`.
- Rule 10 said `format!("{:?}")` is the same as `Display`. It wraps the
  text in `Screen(…)`, which `Screen::parse`, `diff` and `render` refuse.
- §6 said every error's `Display` ends with the screen. Only the wait
  errors (timeout, EOF, emulator) and a failed write carry one.

SECURITY.md: "bot-authored commits are rejected by CI" is true except for
Dependabot, which CONTRIBUTING's AI tooling policy exempts from the
identity rule only; the provenance line now says so.

DESIGN.md:
- The writer queue is unbounded (`mpsc::channel`), capped on reply bytes;
  one paragraph said bounded, the next unbounded.
- The user-facing Windows list is in docs/LIMITATIONS.md, not the README.
- `CSI 18 t` is answered, and `14 t`/`16 t` once a cell size is declared,
  so "the non-pixel `CSI t` reports" were never all unanswered; the same
  correction in LIMITATIONS.md names `11 t` and `19 t` instead.
- The compatibility corpus starts at 0.10.1, not "each published release".

emit's steps: an argument beginning with `--` must be a step, so `emit
--foo` exits 2 rather than printing `--foo`; TEXT and `--text` say so.

Rustdoc:
- `Unsupported`'s summary was `Clipboard`'s first sentence, so docs.rs
  described the type as clipboard data; the sentence is back on
  `Clipboard`, which had no summary.
- `assert_screen_snapshot!` repeated the Display-versus-Debug reason that
  #559 corrected in the skill.
- `wait_frame` pointed at a README Windows note that is not in the README.
- `Screen`'s Debug impl said "exactly like Display"; it adds `Screen(…)`.
- Style, not a contradiction: `mask_matching` carried the crate's only
  `# Examples` heading; the rest of the crate's doctests have none.

Found by a documentation sweep; the same sweep found the relative
`current_dir` bug and the inspect help errors fixed in #561.

Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com>
vyncint added a commit that referenced this pull request Oct 7, 2026
Each was checked against the code or by running it before it changed;
one more item is a style tidy, marked as such.

README:
- `Screen::parse` reads the snapshot format, not an insta `.snap`: it
  rejects the `---` header on line 1. The body below it parses, and the
  `termlens` command reads a whole `.snap` because it strips the header.
- The skill has five recipes, not four.

SKILL.md:
- §7's mask row said `mask_matching` "spans a wrap the way `find_all`
  does". A value split by a soft wrap is two rows and stays unmasked
  (measured: 10 columns, `SECRET-VALUE-42`); a needle with `\n` is what
  spans rows. Rule 12 already says so for `contains`.
- Rule 10 said `format!("{:?}")` is the same as `Display`. It wraps the
  text in `Screen(…)`, which `Screen::parse`, `diff` and `render` refuse.
- §6 said every error's `Display` ends with the screen. Only the wait
  errors (timeout, EOF, emulator) and a failed write carry one.

SECURITY.md: "bot-authored commits are rejected by CI" is true except for
Dependabot, which CONTRIBUTING's AI tooling policy exempts from the
identity rule only; the provenance line now says so.

DESIGN.md:
- The writer queue is unbounded (`mpsc::channel`), capped on reply bytes;
  one paragraph said bounded, the next unbounded.
- The user-facing Windows list is in docs/LIMITATIONS.md, not the README.
- `CSI 18 t` is answered, and `14 t`/`16 t` once a cell size is declared,
  so "the non-pixel `CSI t` reports" were never all unanswered; the same
  correction in LIMITATIONS.md names `11 t` and `19 t` instead.
- The compatibility corpus starts at 0.10.1, not "each published release".

emit's steps: an argument beginning with `--` must be a step, so `emit
--foo` exits 2 rather than printing `--foo`; TEXT and `--text` say so.

Rustdoc:
- `Unsupported`'s summary was `Clipboard`'s first sentence, so docs.rs
  described the type as clipboard data; the sentence is back on
  `Clipboard`, which had no summary.
- `assert_screen_snapshot!` repeated the Display-versus-Debug reason that
  #559 corrected in the skill.
- `wait_frame` pointed at a README Windows note that is not in the README.
- `Screen`'s Debug impl said "exactly like Display"; it adds `Screen(…)`.
- Style, not a contradiction: `mask_matching` carried the crate's only
  `# Examples` heading; the rest of the crate's doctests have none.

Found by a documentation sweep; the same sweep found the relative
`current_dir` bug and the inspect help errors fixed in #561.

Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com>
@vyncint
vyncint merged commit 5f4203e into main Oct 7, 2026
31 checks passed
@vyncint
vyncint deleted the fix/relative-cwd-and-inspect-help branch October 7, 2026 00:25
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