Testing
Start with the smallest check that can prove your change. Then verify the committed patch before pushing. A local pass is not proof of a complete machine installation, and a skipped check is not a pass.
Commands below start at the repository root unless they explicitly enter cli/.
CI and releases explains what runs after a push.
Choosing coverage
Section titled “Choosing coverage”| Change | Start with | Broaden coverage when… |
|---|---|---|
| Documentation or skills | Local docs stage | Headings, paths, or selectors changed: check incoming references too; site changes also need a visual review |
| TOML, categories, or source layout | config_drift and local config stage | Loading, merging, or profile behavior changed |
| Task selection or dependencies | Affected command suite and task_execution | Shared catalog or scheduler behavior changed |
| A concrete resource | Domain unit tests and affected lifecycle_contracts / e2e_apply cases | Shared planning, adapters, or failure accounting changed |
| Process execution | cargo test --locked --profile ci infra::exec:: from cli/ | Timeout, cancellation, or native process behavior changed: test Windows too |
| Wrappers or hooks | Isolated wrapper/hook fixtures | Integration behavior changed: use a disposable environment |
| Desktop or application content | Desktop shell / Application configuration | Rendering, focus, or session behavior needs native testing |
| CI selection or workflows | Local ci stage and the changed job’s command | Dependencies or triggers changed: include classification fixtures |
| Arch package recipes | Recipe validation | A real package build/install is needed: use disposable Arch |
For Rust changes, also run formatting and host Clippy. Add Windows-target Clippy for platform-sensitive code, and the full Rust suite for shared configuration, catalog, or engine changes. Use the pinned toolchain.
Isolation and side effects
Section titled “Isolation and side effects”| Kind of check | What it can change |
|---|---|
| Fixture tests | Temporary files, repositories, and subprocesses within controlled roots |
| Repository checks | Build caches and sometimes network access; CLI checks can write logs and selection metadata |
| Host integration | Real home files, packages, services, registry values, or Git history |
Do not run update, remove, package updates, elevation, or desktop restarts to
compensate for missing tests. Full install/remove and application integration
belong in disposable homes/runners/VMs. The full commit-hook suite also creates
commits; a clean developer checkout is not a safe fixture.
Fast local sequence
Section titled “Fast local sequence”The canonical entrypoints are
check.sh and
Check.ps1. Select stages for
your patch rather than always running everything.
Linux:
sh .github/workflows/scripts/linux/check.sh --listsh .github/workflows/scripts/linux/check.sh --strict docssh .github/workflows/scripts/linux/check.sh --strict fmt clippy test configWindows:
pwsh -File .github\workflows\scripts\windows\Check.ps1 -Listpwsh -File .github\workflows\scripts\windows\Check.ps1 -Strict fmt clippy test configWith no stage names, the scripts run their default stages:
| Stage | What it checks |
|---|---|
fmt | Rust formatting |
clippy | Host Clippy for all targets, with warnings rejected |
test | Full Rust test suite, not shell/PowerShell integration scripts |
rustdoc | Private-item documentation and configured rustdoc lints |
config | CLI configuration, source paths, and required config files |
docs | Markdown links, skill metadata/fixtures, task selectors, and the Starlight build, published links, and browser regressions; Linux entrypoint only |
ci | Workflow contracts, path/release classification, aggregate gating, and actionlint; Linux entrypoint only |
shell | Shared ShellCheck discovery and rules |
powershell | PSScriptAnalyzer at Warning/Error severity |
audit / deny | Dependency advisories / dependency policy |
Cargo verification uses --locked and the ci profile where applicable.
The scripts use tools from PATH; they do not select the pinned compiler for
you. Compiler-dependent checks reject the wrong Rust version.
Missing tools usually produce SKIP. --strict, -Strict, or
DOTFILES_CHECK_STRICT=1 makes skips fail. An installed tool’s nonzero exit is
a failure, including exit code 2. See
Analyzer prerequisites for required tools and the
PowerShell entrypoint difference.
Two stages are opt-in: msrv attempts to install and compile with the minimum
Rust version from Cargo.toml; Linux qml runs Qt Quick Test. --all on Linux
includes both, while Windows -All adds msrv. These options can require
downloads or additional tools; they do not run mutation tests or host integration.
The docs stage needs Python, Cargo, the Node version in docs/.node-version,
and the site dependencies and Chromium runtime.
Its selector check runs a Rust fixture.
It also validates repository skill metadata and runs the validator’s synthetic
regressions without third-party Python dependencies. The supported frontmatter
convention and manual routing cases live in
Skill maintenance.
Metadata checks do not measure agent instruction effectiveness.
The source link check inspects tracked and unignored new Markdown files, including unstaged moves. It checks local inline links, not external URLs, reference-style links, Mermaid rendering, or the truth of the prose. A quick link-only check is:
python3 -B .github/workflows/scripts/linux/check-doc-links.py .Generated site output and node_modules are ignored. New documents do not
need to be staged to check their links.
Documentation site
Section titled “Documentation site”Install the locked dependencies and browser runtime once:
cd docsnpm cinpx playwright install chromiumOn a minimal Linux machine, Playwright may report missing system libraries.
Its install --with-deps chromium command can install those libraries, but that
is a host package change: use an authorized environment or a disposable runner.
CI uses that option on its disposable Ubuntu runner.
For site-only changes, run from the repository root:
DIR="$PWD" sh .github/workflows/scripts/linux/test-docs.sh docs_links docs_siteThe site check runs Astro diagnostics, Markdown-transform fixtures, the static build, matching page titles/navigation/filenames, published URL/anchor/asset, canonical sitemap, and robots/indexability checks. Browser regressions exercise the homepage, top-level topic navigation, search, theme persistence, warning callouts, and every Mermaid diagram at desktop and mobile sizes. Diagram labels must stay at least 14 CSS pixels. Mobile emulation is not a native phone/browser test.
The browser suite starts and stops its own loopback preview on port 4322; keep that port free. For manual preview and changes to the content pipeline, see Documentation site.
Before pushing
Section titled “Before pushing”Commit the intended patch, then run the manual Linux pre-push checker (Python 3.11 or newer):
python3 -B .github/workflows/scripts/linux/pre-push.py --planpython3 -B .github/workflows/scripts/linux/pre-push.pyThe default range is origin/main..HEAD. Make sure that remote-tracking ref is
current. For another destination or base, pass --base <remote-sha> and
optionally --head <commit>. The plan prints the selected checks without running
them. An invalid range fails; an empty range reports that no checks ran.
The checker exports the selected commit into a temporary Git checkout and runs CI-selected local checks with strict skip handling. Unstaged fixes cannot satisfy a committed patch. It reuses the Cargo target cache and redirects CLI state and logs into temporary directories.
Have the pinned compiler on PATH and required tools, MSRV, Windows target,
and Playwright Chromium runtime installed first; this checker does not install
them. It restores locked npm dependencies inside the isolated checkout.
Cargo and dependency checks may still need network/cache access. It is a
manual command, not an installed Git hook, and does not push.
Resolve selected local failures before publishing. Its REMAINING CI list
identifies native Windows, host integration, mutation, or publication work left
to CI. Report unavailable checks explicitly. After pushing, inspect the
exact commit’s aggregate and informational results.
Integration test suites
Section titled “Integration test suites”Rust integration targets under cli/tests/ use fixtures.
They are different from CI’s host-mutating integration jobs.
| Target | Main responsibility |
|---|---|
config_drift | Real configuration, source, and category invariants |
domain_boundaries | Architectural dependencies and platform/environment boundaries |
update_command, remove_command, list_command, check_command | Command membership, selection, loading, and outcomes |
task_execution | Task/resource convergence and dry-run |
task_output | Statuses, reasons, logs, and exit behavior |
lifecycle_contracts | Preview, apply/repeat, retry, and conservative removal across real tasks |
e2e_apply | CLI convergence against controlled state |
behavioral_ci | Cross-cutting regressions for CI assumptions |
Combine related targets in one invocation:
cd clicargo test --locked --profile ci --test config_drift --test check_commandcargo test --locked --profile ci --test update_command -- --listcargo test --locked --profile ci --test update_command repositoryThe last command filters by a test-name substring. Check the test count: a misspelled filter can run zero tests and still exit successfully.
Test ownership
Section titled “Test ownership”Put a regression at the lowest boundary that proves the behavior. Use unit tests for parsing, state transitions, resource errors, and adapters; command tests for selection, dependency wiring, elevation policy, and exit status; lifecycle/end-to-end fixtures for state across runs.
Shared sequential/parallel scheduler contracts live in
scheduler/conformance.rs.
Adjacent tests cover synchronization and output ordering;
batch_reports.rs and
parallel.rs cover partial failure,
cancellation, and in-flight accounting.
Use common::cli_command for CLI subprocess
fixtures. It isolates home/config/cache/state/logs, clears inherited
DOTFILES_* and GIT_* overrides, sets a fixture Git boundary, and disables
self/repository updates. Use injected environments/executors and isolated
loggers for in-process tests.
common/lifecycle.rs provides real
symlink, Git-hook, and Git-setting lifecycle fixtures. Assert exact changes,
counts, preserved state, and behavior across fresh contexts, not only a success
message. Named case tables help repeated input/rejection cases; keep separate
cases where platform contracts differ.
CLI validation
Section titled “CLI validation”To check this checkout using the current source, without wrapper bootstrap:
cd clicargo run --locked --profile ci -- check --root .. --profile desktop \ --only config-diagnostics,symlink-sources,config-filesThese commands explicitly select this checkout with --root ...
This is the local config stage. It fails on configuration diagnostics, checks
configured symlink/permission sources including retained inactive definitions,
and verifies required TOML files. Structural conflicts can fail during loading,
before a check task runs.
To include external plugin and script checks:
cd clicargo run --locked --profile ci -- check --root .. --profile desktop --verboseThat adds APM pack --dry-run --verbose, ShellCheck, and PSScriptAnalyzer where
applicable. Discovery and file-read failures are not treated as empty input.
Add --overlay /path/to/private-dotfiles to validate combined configuration.
Keep its diagnostics private until sanitized: a public-only check cannot prove
an overlay has no conflicts.
Analyzer prerequisites
Section titled “Analyzer prerequisites”| Check | Required tools |
|---|---|
| Local APM package validation | apm |
| ShellCheck | shellcheck and POSIX sh |
| PSScriptAnalyzer | PowerShell 7 (pwsh) and the PSScriptAnalyzer module |
Missing tools produce an unmet skip in CLI checks. --fail-on-skip or a present
CI variable makes those skips fail. Windows PowerShell does not substitute
for pwsh in the analyzer.
If pwsh is available but PSScriptAnalyzer is absent or broken, CLI checks,
the Linux local checker, and the commit hook fail. The Windows local
Check.ps1 entrypoint instead skips a missing module; -Strict makes that
skip fail too. Install the prerequisites for the checks you intend to run.
Dry-run testing
Section titled “Dry-run testing”Preview the smallest affected selection with the current build:
cd clicargo run --locked --profile ci -- update --root .. --profile desktop \ --only symlinks --dry-run --verboseFor removal, use remove instead of update.
Substitute agent-packages or another selector as appropriate.
A preview must not apply normal managed-state changes, but it can inspect the host and write logs or selection metadata. Overlay scripts execute their own check/preview code. Only preview trusted configuration.
Prove non-mutation using fixture assertions on files, commands, and state; the words “dry run” in output are not proof. See Private overlays.
Focused mutation testing
Section titled “Focused mutation testing”Mutation testing temporarily changes code to check whether tests notice a wrong branch, lost error, or incorrect preview. Consider it for behavior changes, especially failure handling, scheduling, and partial success.
Run cargo-mutants in a disposable checkout, never the shared working copy.
Inspect each MISSED result: add an assertion for observable behavior or explain why the mutation
is equivalent. Ordinary tests and --all do not run mutation testing.
With cargo-mutants installed, select the affected module in that disposable
checkout. For example:
cd clicargo mutants --file src/app/filter.rs --cargo-arg=--profile=ci --cargo-arg=--lockedUse --in-diff with a unified Git diff for changed-line coverage. Record any
narrowed scope and inspect mutants.out before cleaning up. CI handles its
changed-code selection, shards, and native-code exclusions; see
Mutation coverage instead of copying that setup into
each local command. Linux checks do not provide native Windows mutation coverage.
Wrapper and hook tests
Section titled “Wrapper and hook tests”Isolated regressions
Section titled “Isolated regressions”These use fixture tools rather than bootstrapping or installing the real CLI:
sh .github/workflows/scripts/linux/test-hook-inputs.sh \ test_staged_ci_guards test_ci_change_classification test_staged_ci_guard_deletionssh .github/workflows/scripts/linux/test-shell-wrapper.sh \ test_wrapper_preserves_runtime_context test_wrapper_uses_cargo_artifactWith no selector, test-hook-inputs.sh runs all input regressions. Its build
metadata case needs the pinned Rust compiler; without it, that case skips.
On Windows, dot-source the wrapper suite to load only the isolated functions:
. .\.github\workflows\scripts\windows\Test-ShellWrapper.ps1Test-IsolatedWrapperPathTest-CargoArtifactPathFull wrapper suites can build/copy binaries or use BINARY_PATH. Read their
setup before running all cases.
Checking your real index
Section titled “Checking your real index”These inspect staged content and do not create commits:
sh hooks/check-sensitive.shsh hooks/check-rust.shsh hooks/check-ci-guards.shDOTFILES_HOOKS_FULL=1 sh hooks/pre-commitThe Rust and CI guards export the index and reuse caches without stashing or overwriting working files. Full mode remains staged-change-aware; it is not committed-range validation. See Git hooks.
Full commit-hook suite
Section titled “Full commit-hook suite”Never run test-git-hooks.sh in your developer checkout. It creates real
commits. A clean tree is not a safe fixture; do not use its force override.
The full suite runs in CI’s disposable checkout. Prefer the isolated local regressions above; use a disposable repository and home if you need full integration coverage. Do not reuse a failed fixture, because its existing commits can make a later check see an empty diff.
Cleanup retention and Git templates
Section titled “Cleanup retention and Git templates”python3 -B .github/workflows/scripts/linux/test-maintenance.pyThese fixtures check merge/rebase cleanup, protected branches, worktrees, and file/link/directory retention in temporary repositories and directories. They do not start services or delete real user files.
Desktop shell
Section titled “Desktop shell”QML tests need Qt 6.8 or newer Quick Test and use inert Quickshell mocks:
sh .github/workflows/scripts/linux/check.sh --strict qmlPython helpers, lock scripts, and stock-cache concurrency use isolated fixtures:
python3 -B -m unittest discover \ -s symlinks/config/quickshell/tests/python -p 'test_*.py'python3 -B -m unittest discover \ -s symlinks/config/hypr/scripts/tests -p 'test_*.py'sh .github/workflows/scripts/linux/test-stocks.shOffscreen tests cover logic/layout, not native rendering, focus, or a live
compositor/network session. Helpers mock desktop actions and market requests.
On Windows use python -B; Linux-only cases skip. Record native manual
coverage separately.
Application configuration
Section titled “Application configuration”These use isolated homes and mock commands:
python3 -B .github/workflows/scripts/linux/test-shell-config.pypython3 -B -m unittest discover -s symlinks/vim/tests -p 'test_*.py'pwsh -NoProfile -File symlinks/config/powershell/tests/Test-Prompt.ps1The PowerShell fixture covers first-start discovery, custom Cargo paths, literal profile-fragment directories, and PATH deduplication on reload. Install required native tools; a missing-tool skip gives no coverage.
CI gates
Section titled “CI gates”The ci-success gate checks that selected jobs succeeded and unselected jobs
were intentionally skipped. See CI and releases for scheduling and
informational jobs; do not interpret an omitted job as a pass.
CI selection fixtures
Section titled “CI selection fixtures”python3 -B .github/workflows/scripts/linux/test-ci-changes.pyThese cover classification, aggregate gating, publishing contracts,
renames/deletions, unusual paths, and invalid ranges. Temporary Git repositories
and mocked gh calls keep fixture commits and tags local; nothing is published.
Use ClassificationTests GateTests as arguments for just those cases.
Also run the local ci stage when workflow syntax or contracts change.
Release selection
Section titled “Release selection”Publishing compares the exact tested commit to the last published release, not merely the previous push. See Release selection for why a docs-only push can recover a previously failed release.
Platform coverage parity
Section titled “Platform coverage parity”Linux cross-target Clippy catches Windows type/import errors:
cd clicargo clippy --locked --profile ci --target x86_64-pc-windows-gnu \ --all-targets -- -D warningsIt does not exercise Windows registry, UAC, symlink/junction, path, or process behavior. Native Windows tests are still required. The CI coverage matrix lists those boundaries.
Executor tests launch their ignored child-process fixture themselves; do not
run it manually with --ignored.