epsh 0.0.6

embeddable posix shell
Documentation
# TODO

Detailed backlog for shell correctness work that is important, but deferred.

This file is intentionally biased toward semantic and OS-observable correctness,
not feature growth.

## Priority Order

1. Expansion and quoting exactness
2. `errexit` / special builtin semantics
3. Redirection / fd / process hygiene
4. Signal, trap, and wait behavior
5. `PWD` and path semantics
6. PATH / env merge semantics
7. Command substitution edge cases
8. Glob / pattern exactness
9. NUL-byte policy and API consistency
10. Targeted regression tests and capability matrices

## 1. Expansion And Quoting Exactness

Goal: prove and preserve the exact expansion pipeline and quoting semantics.

### Expansion pipeline audit

- Audit every expansion path against POSIX and current dash-like intent:
  `tilde -> param -> command substitution -> arithmetic -> field splitting -> glob -> quote removal`.
- Verify the code does not accidentally perform quote removal too early.
- Verify quoted regions suppress field splitting and globbing exactly where intended.
- Verify `${...}` forms preserve the correct quoting context in nested cases.
- Verify trim operators (`%`, `%%`, `#`, `##`) use pattern semantics, not plain substring semantics.
- Verify `${var+word}`, `${var-word}`, `${var=word}`, `${var?word}` respect quoted/unquoted context for `word`.
- Verify `$@` and `"$@"` preserve field boundaries exactly.
- Verify `$*` and `"$*"` differ correctly with current `IFS`.
- Verify empty expansions in quoted and unquoted contexts behave correctly.

### Quoting edge cases

- Add targeted tests for interactions among:
  - single quotes
  - double quotes
  - backslash escaping
  - command substitution
  - parameter expansion
  - arithmetic expansion
- Test nested quoting inside `"${...}"`.
- Test escaped glob metacharacters surviving until the right phase.
- Test escaped newline continuation rules in all contexts that support them.
- Test heredoc quoting modes carefully:
  - quoted delimiter
  - unquoted delimiter
  - tab-stripping with `<<-`
  - backslash-newline continuation in heredoc bodies

### Regression matrix

- Build a table of representative cases for:
  - unquoted
  - double-quoted
  - single-quoted
  - assignment context
  - command substitution context
  - pattern context
- Keep these as integration tests instead of only unit tests.

## 2. `errexit` / Special Builtin Semantics

Goal: make failure behavior boringly predictable and script-compatible.

### `set -e` / `errexit`

- Expand integration coverage for `set -e` in:
  - simple commands
  - pipelines
  - `if` conditions
  - `while` / `until` conditions
  - `&&` left-hand side
  - `||` left-hand side
  - subshells
  - functions
  - command substitutions
  - grouped commands
- Verify only leaf execution points trigger exit, not compound syntax nodes.
- Verify tested contexts suppress `errexit` exactly where intended.
- Verify `! cmd` inverts status without producing accidental `errexit`.
- Verify pipeline + `pipefail` interactions under `set -e`.

### Special builtins

- Audit which builtins are treated as POSIX special builtins versus normal builtins.
- Verify assignment prefixes persist for special builtins and do not persist for normal builtins/external commands.
- Verify redirection failure on a special builtin is a shell error with the correct status.
- Verify parse/runtime errors around special builtins affect shell state correctly.
- Add tests for:
  - `export`
  - `readonly`
  - `unset`
  - `eval`
  - `.`
  - `exec`
  - `set`
  - `trap`

### Exit statuses

- Audit every shell-generated error path to ensure statuses are intentional and consistent.
- Verify usage errors vs runtime errors vs command-not-found vs not-executable are distinct where expected.
- Verify builtin misuse returns the intended status.

## 3. Redirection / FD / Process Hygiene

Goal: ensure file descriptor behavior is exact, leak-free, and race-resistant.

### Redirection ordering

- Add tests for left-to-right redirection semantics:
  - `cmd >out 2>&1`
  - `cmd 2>&1 >out`
  - `cmd <in >out`
  - multiple redirections to same fd
- Verify duplication and closure ordering:
  - `n>&m`
  - `n<&m`
  - `n>&-`
  - `n<&-`
- Verify redirections apply correctly to:
  - builtins
  - functions
  - external commands
  - `exec`

### Heredoc plumbing

- Audit heredoc stdin ownership and lifetime.
- Add tests for:
  - multiple heredocs on one command
  - heredoc with builtins
  - heredoc in pipelines
  - heredoc with command substitution in body
- Verify no accidental fd reuse or premature close.

### FD leaks

- Add regression tests that children do not inherit unrelated descriptors.
- Add explicit `CLOEXEC` coverage where feasible.
- Audit all pipe, dup, open, and tempfile-like paths for leak resistance.
- Verify saved redirection fds are always restored or intentionally consumed.

### Process spawning

- Verify direct `exec` and spawned child process setup stay equivalent for:
  - env
  - cwd
  - redirections
  - signal dispositions
- Verify failure paths restore shell fds/state correctly.

## 4. Signal, Trap, And Wait Behavior

Goal: make process control and traps behave like a real shell, especially during waits.

### Trap timing

- Revisit the current known limitation:
  traps fire between commands but not during blocking waits.
- Define the intended model explicitly.
- Add tests for traps arriving during:
  - foreground external command wait
  - pipeline wait
  - command substitution wait
  - builtin-heavy loops

### Signal disposition and propagation

- Verify children get default signal dispositions where expected.
- Verify shell-installed handlers do not leak into exec'd children.
- Verify signal interruption of builtins and waits yields intended statuses.
- Verify `SIGINT`, `SIGPIPE`, `SIGTSTP`, and `SIGCHLD` handling semantics.

### Job-control-adjacent behavior

- Even if full job control is not a goal, verify:
  - process groups for foreground pipelines
  - stopped process propagation
  - `WUNTRACED` handling in interactive mode
  - terminal handoff/restore correctness

## 5. `PWD` And Path Semantics

Goal: eliminate ambiguity around logical vs physical cwd behavior.

### Define shell policy

- Decide explicitly whether `epsh` is:
  - logical by default (`PWD` tracks user path)
  - physical by default (`PWD` tracks kernel-resolved path)
  - mixed, with documented command-specific behavior
- Document how `cd`, `pwd`, and `$PWD` relate.

### Consistency audit

- Verify `cd` updates shell cwd state, process cwd state, and `PWD` consistently.
- Verify `pwd` output matches the chosen logical/physical policy.
- Verify relative path resolution for:
  - redirections
  - `.`
  - glob
  - PATH search helpers
  - `test`
  - command substitution work dirs
- Audit every use of `canonicalize`, `current_dir`, and `to_path_buf` for policy violations.

### Symlink coverage

- Add tests around symlinked directories:
  - `cd symlink && pwd`
  - `cd symlink/..`
  - relative opens after logical `cd`
  - interaction with `.` and redirections

## 6. PATH / Env Merge Semantics

Goal: make env assembly and PATH lookup semantics explicit and testable.

### Child environment assembly

- Audit precedence rules among:
  - inherited foreign env entries
  - exported shell vars
  - prefix assignments
- Verify overwrites are deterministic and byte-correct.
- Verify unexported shell vars never leak to children.
- Verify `exec` uses the same merge rules as normal spawn.

### PATH lookup

- Add more coverage for:
  - empty PATH segments
  - relative PATH entries
  - PATH updates during shell lifetime
  - command hashing assumptions, if any are introduced later
- Verify `command -v`, `type`, external exec, and builtin `which`-style paths stay aligned.

### Environment corner cases

- Add tests for inherited non-shell env names that should be preserved to children.
- Verify shell identifier rules are separate from foreign env preservation rules.
- Audit environment import/export behavior under `env_clear` builder mode.

## 7. Command Substitution Edge Cases

Goal: make `$(...)` and backticks reliable under nesting and failure.

### Output semantics

- Verify trailing newline trimming exactly matches intended shell behavior.
- Verify interior newlines are preserved.
- Verify invalid UTF-8 bytes survive unchanged except for required newline trimming.

### Execution semantics

- Test nested command substitutions.
- Test command substitution inside double quotes and unquoted context.
- Test command substitution failures under `set -e`.
- Test variable assignments and side effects inside command substitutions.
- Verify stderr routing and redirection behavior.

### Resource behavior

- Audit pipe/fork setup in command substitution to avoid leaks and deadlocks.
- Verify builtins optimized into command substitution still match external behavior where required.

## 8. Glob / Pattern Exactness

Goal: ensure pathname expansion and pattern matching are stable and compatible.

### Pathname expansion

- Add tests for:
  - ordering guarantees
  - dotfile matching rules
  - nested directories
  - escaped metacharacters
  - bracket expressions
  - ranges and negated classes
  - dangling symlinks
  - mixed UTF-8 and invalid-byte filenames
- Verify unmatched globs stay literal when intended.
- Verify `set -f` / `noglob` disables pathname expansion everywhere it should.

### Pattern matching helpers

- Audit `fnmatch` behavior used by:
  - parameter trim operators
  - `case`
  - glob
- Add shared regression cases so these contexts do not drift semantically.

## 9. NUL-Byte Policy And API Consistency

Goal: be explicit about the one byte Unix process/file APIs do not permit in strings.

### Policy

- Document clearly:
  - shell data may contain arbitrary bytes except NUL where OS APIs forbid it
  - errors on NUL are expected boundary errors, not encoding bugs
- Ensure all public APIs fail consistently on embedded NUL where required.

### API review

- Audit byte-safe APIs for consistent naming and behavior:
  - `set_var_bytes`
  - `get_var_bytes`
  - `set_args_bytes`
  - path resolution helpers
  - exported env helpers
- Decide which UTF-8 convenience APIs remain and how they should document lossy assumptions.

### Tests

- Add targeted tests that NUL-containing data:
  - is rejected cleanly at exec boundaries
  - does not cause panics
  - does not partially mutate shell state before failing

## 10. Regression Tests And Capability Matrices

Goal: keep future changes honest with OS-observable tests, not just unit coverage.

### Byte-correctness matrix

- Maintain the capability matrix from `OSSTRING.md` with rows:
  - argv to child
  - env to child
  - inherited env
  - script path
  - redirection output
  - redirection input
  - `cd`
  - `test`
  - `.`
  - PATH search
  - glob
  - command substitution
- Keep columns for:
  - ASCII
  - valid UTF-8 non-ASCII
  - invalid UTF-8 bytes

### Fixture strategy

- Keep test fixtures as repo-native Rust binaries.
- Prefer helpers that expose raw OS observations:
  - argv bytes
  - env bytes
  - path bytes + stat result
- Avoid helper layers that reintroduce UTF-8 assumptions.

### Cross-environment realism

- Continue skipping raw-path tests on hosts/filesystems that reject invalid path bytes.
- Add comments to each such test explaining what condition caused the skip.
- Keep byte-oriented env/argv tests unconditional because they do not depend on filesystem acceptance.

### Test organization

- Group high-value shell-correctness tests by semantic area instead of only by implementation file.
- Prefer integration tests over unit tests when behavior crosses parser/expander/evaluator/OS boundaries.
- When fixing a semantic bug, add:
  - a narrow regression test for the exact bug
  - at least one nearby matrix-style test to guard the broader behavior

## Deferred Nice-To-Haves

These matter less than the items above, but are worth tracking.

- More exhaustive mksh/dash differential coverage for edge semantics.
- Dedicated stress tests for fd churn and nested substitutions.
- Optional property-style tests for pattern matching and field splitting.
- A short design note documenting which dash/posh/mrsh behaviors are intentionally followed and where `epsh` diverges.