Quirlv0.5.1
Contributing

Contributing to Quirl

Canonical Quirl project documentation synced from CONTRIBUTING.md.

Thanks for helping make Quirl a better shell. Quirl is a fast-moving Rust prototype with a deliberately narrow Unix-first support contract. Small, well-tested changes that extend an existing pattern are especially welcome.

Before you start

  • Read AGENTS.md for the crate layering, error, Lua sandbox, generated-artifact, and test rules. These rules apply to human and automated contributions alike.
  • Check the relevant architecture decisions in docs/decisions before changing a settled boundary.
  • Use a public issue or discussion for product proposals. Report suspected vulnerabilities privately as described in SECURITY.md.

Quirl targets Linux and macOS for interactive use. Windows portability is kept contract-tested on a best-effort basis, but native Windows terminal behavior is not a release gate. The exact boundary is recorded in ADR 0010.

Set up the workspace

Install Rustup. The repository's rust-toolchain.toml selects Rust 1.99.0; Cargo builds the repository-local xtask, and no separate task runner or system Lua installation is needed. On Unix, that Rust-owned xtask also runs the bounded real-PTY harness in xtask/src/pty.rs and xtask/src/rich_pty.rs; no separate scripting-language toolchain is required.

Editors that do not bundle Rust Analyzer can use the server and standard-library sources matched to the pinned toolchain. These editor-only components stay optional so CI and command-line-only contributors do not download them:

rustup component add rust-analyzer rust-src
git clone git@github.com:niklas-heer/quirl.git
cd quirl
cargo xtask check
cargo run -p quirl-cli

cargo xtask check is the canonical local gate and must pass before every commit. It checks formatting, Quirl source formatting, Clippy, all workspace tests, the real-PTY interaction matrix on Unix, and the guest-side Lua tests. The workspace denies Clippy's pedantic and nursery groups behind an explicit compatibility allowlist, plus production safety restrictions such as unchecked casts, panics, placeholder macros, and unreachable branches. Test-only unwrap, panic, and indexing allowances live in clippy.toml; do not weaken the workspace policy to make production code compile. Generated shell cases use a stable seed; replay or expand them with cargo xtask test --seed <seed> --cases <count>. Stateful compatibility swarms run with cargo xtask simulate --seed <seed> --sessions <count>, producing an inspectable JSONL trace. The complete layered strategy is in docs/testing-strategy.md. Pull requests and pushes to main run the canonical gate in CI with bounded test parallelism; the larger replayable Bash/Zsh simulation swarm runs separately each day. Include the local cargo xtask check result in your pull request so failures are caught before CI.

Prefer package-targeted commands during iteration so research-only workspace members such as quirl-bench are not rebuilt unnecessarily:

cargo check -p quirl-ui
cargo test -p quirl-ui
cargo build -p quirl-cli

At the workspace root, a bare cargo build defaults to quirl-cli; use an explicit -p selection or --workspace when another scope is intended. The default development profile keeps line numbers for workspace backtraces while omitting dependency debug data. Use cargo build --profile debugging when a full-debug build is needed.

For a faster edit-test loop, install the optional developer tools and use the checked-in project configuration:

cargo install --locked cargo-nextest --version 0.9.143
cargo install --locked bacon --version 3.25.0
cargo nextest run --workspace
bacon                 # Clippy on each relevant change
bacon nextest         # Isolated tests with slow-test reporting

Nextest reports any test taking longer than two seconds as slow without killing it, so timing regressions are visible while each test's own resource and cleanup limits remain authoritative. Bacon also provides check-all and canonical jobs. The latter runs the full cargo xtask check gate; neither tool replaces that required pre-commit check.

The developer-only xtask binary uses color-eyre for readable error chains, source locations, and panic reports. Set RUST_BACKTRACE=1 when a task failure needs a backtrace. Product crates continue to return the stable, serializable quirl_core::ShellError contract.

Useful focused tasks are typed Rust subcommands in xtask:

cargo xtask fmt
cargo xtask lint
cargo xtask test
cargo xtask demo
cargo xtask clean

Debug builds with tests grow to tens of gigabytes. cargo xtask clean removes debug builds and test binaries but keeps release builds; cargo xtask clean --all also removes release builds, Rustdoc, and the website's Next.js build.

The release recording is intentionally separate: cargo xtask demo-record <sha256> consumes the already-built candidate only after verifying its digest and local VHS/font prerequisites. It never rebuilds the artifact during capture.

Make a change

  • Put functionality in the lowest crate that can own it; never invert the dependency graph to make a change compile.
  • Reuse quirl_core::ShellError, LuaPolicy, HOST_API, and Catalog::builtin() rather than introducing parallel mechanisms.
  • Keep tests beside the implementation in #[cfg(test)] mod tests, and name them as behavior sentences in snake case.
  • Add adversarial tests for sandbox, resource-limit, terminal-safety, or parser boundary changes.
  • Add a frozen regression before fixing a generated or differential failure, and include its seed and case index in the change description.
  • Do not hand-edit docs/quirl.lua. Change HOST_API, run cargo xtask sdk, and check in the regenerated file with its source change.
  • Do not add a feature flag, dependency edge, unsafe block, or significant architecture choice without documenting why it belongs.

For user-visible behavior, check both a normal terminal and the text fallback where practical:

NO_COLOR=1 cargo xtask demo
TERM=dumb NO_COLOR=1 cargo xtask demo

Prompt glyphs and color must remain decorative; state and diagnostics need a legible plain-text representation.

Send the change

Use a present-tense Conventional Commit subject, optionally scoped, such as fix(lua): bound completion output. Keep commits focused and avoid unrelated generated or formatting churn.

A pull request should say:

  • what user or developer problem it solves;
  • which public contract, security boundary, or generated artifact it changes;
  • what was tested, including the cargo xtask check result;
  • what remains intentionally out of scope.

Release work follows docs/release-checklist.md. Passing an older commit or a rebuilt artifact is not evidence for the current release candidate.

On this page