Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
a5c95bc
ci: add non-blocking Windows build-test job
leynos Aug 14, 2026
38a40e6
fix: gate which env capture chain for Windows production path
leynos Aug 14, 2026
7c0acf2
fix: gate test_support items unused on Windows
leynos Aug 14, 2026
91cc635
fix: clear Windows-only Clippy and test-build findings
leynos Aug 14, 2026
a63a6f8
docs: record Windows CI job and PATHEXT cfg widening decision
leynos Aug 14, 2026
40154ae
fix: own the basename in windows workspace candidate collection
leynos Aug 14, 2026
4caedb3
fix: gate fixture import and use method reference in windows basenames
leynos Aug 14, 2026
4b8c589
ci: make Windows build-test job a blocking merge gate
leynos Aug 14, 2026
e5e2dea
fix: clear Windows-only Clippy and dead-code findings surfaced by blo…
leynos Aug 15, 2026
1a935b2
fix: gate Unix-only runner tests and ungate platform-independent glob…
leynos Aug 15, 2026
4342fc4
fix: clear Windows-only clippy findings in test helper stubs
leynos Aug 15, 2026
2bf3a94
fix: clear Windows-only clippy findings in std_filter_tests
leynos Aug 15, 2026
5a8edc4
style: rustfmt the gated anyhow import in path_filters
leynos Aug 15, 2026
846d413
fix: clear next layer of Windows-only findings
leynos Aug 15, 2026
34a0300
ci: shim whitaker for Git Bash on the Windows job
leynos Aug 15, 2026
a96c93e
fix: route Windows grep-stream test through test_support::fs
leynos Aug 15, 2026
29bfa9b
fix: canonicalise discovery paths with dunce on Windows
leynos Aug 15, 2026
f48f875
Revert "fix: canonicalise discovery paths with dunce on Windows"
leynos Aug 15, 2026
8291a2e
ci: do not persist credentials on the Windows checkout step
leynos Aug 16, 2026
a1b70d3
ci: hoist NEXTEST_VERSION to workflow scope so the documented sed ext…
leynos Aug 16, 2026
d1ebfcf
test: add behavioural workflow-contract tests for the Windows CI job
leynos Aug 16, 2026
51d0c7e
fix: resolve Windows CI failures, setup-rust warning, and coverage gate
leynos Aug 16, 2026
5d645e4
docs: fix caller count, Windows CI statement, and test exception style
leynos Aug 16, 2026
1e2ae88
Wire CodeScene to the generated LCOV report (#518)
leynos Aug 17, 2026
af918ff
Harden Windows CI workflow contracts (#518)
leynos Aug 18, 2026
ee78714
Fix Windows logging-test imports (#518)
leynos Aug 18, 2026
b575b18
Scope stderr routing tests to Unix (#518)
leynos Aug 18, 2026
f8a0d5a
Deduplicate fallback project config layers (#518)
leynos Aug 18, 2026
25f2019
Fix Windows dyndep path retention (#518)
leynos Aug 18, 2026
7b1d277
Harden Windows CI review contracts (#518)
leynos Aug 18, 2026
c005e6a
Fix Windows cache path validation (#518)
leynos Aug 18, 2026
973279a
Fix Windows cache path validation (#518)
leynos Aug 18, 2026
0cf13a6
Eliminate Windows CI lint warnings (#518)
leynos Aug 18, 2026
37757bb
Wire uv cache invalidation to Makefile (#518)
leynos Aug 18, 2026
537ca60
Reject Windows rooted cache paths (#518)
leynos Aug 18, 2026
201b3bd
Fix Windows cache-path diagnostics (#518)
leynos Aug 18, 2026
0fcc324
Fix Windows fake Ninja fixture (#518)
leynos Aug 18, 2026
d787ac1
Compare BDD paths semantically (#518)
leynos Aug 18, 2026
1d33a42
Preserve quoted Windows shell commands (#518)
leynos Aug 18, 2026
39520b9
Compare BDD output paths natively (#518)
leynos Aug 18, 2026
25fe2d6
Stabilize Windows CLI help name (#518)
leynos Aug 18, 2026
f592ca2
Test Windows Ninja fallback natively (#518)
leynos Aug 18, 2026
8870565
Inject Windows Ninja fallback PATH (#518)
leynos Aug 18, 2026
f782bfc
Correct Windows Ninja log expectations (#518)
leynos Aug 18, 2026
b9ab611
Release Windows Ninja temp file handle (#518)
leynos Aug 18, 2026
95ab454
Compare Windows glob target names natively (#518)
leynos Aug 18, 2026
fdfeb62
Stabilize Windows glob test fixtures (#518)
leynos Aug 18, 2026
406d9bc
Restrict command-list shell tests to Unix (#518)
leynos Aug 18, 2026
f5321ab
Retain docs in Unix command-list tests (#518)
leynos Aug 18, 2026
10a0d3f
Limit POSIX command-list process tests to Unix (#518)
leynos Aug 18, 2026
b29ecf6
Scope POSIX Ninja execution tests to Unix (#518)
leynos Aug 18, 2026
bc6f51c
Scope POSIX Ninja execution suites to Unix (#518)
leynos Aug 18, 2026
7e48e5c
Gate Unix fixture import on its consumer (#518)
leynos Aug 18, 2026
a3ba6b9
Normalize package manifest paths on Windows (#518)
leynos Aug 18, 2026
a23e519
Restrict serial runtime tests to Unix (#518)
leynos Aug 18, 2026
9ed953a
Correct Windows shell literal expectation (#518)
leynos Aug 18, 2026
5c1b435
Normalize Windows which fixture paths (#518)
leynos Aug 18, 2026
27880ad
Snapshot Windows which diagnostics (#518)
leynos Aug 18, 2026
223fa40
Make GoReleaser hook contract platform-neutral (#518)
leynos Aug 18, 2026
16c90b6
Preserve non-directory path errors (#518)
leynos Aug 18, 2026
51239ad
Assert manifest errors with native paths (#518)
leynos Aug 18, 2026
e8c01c0
Match Windows candidate diagnostics (#518)
leynos Aug 18, 2026
ba2ed80
Address CodeRabbit review feedback (#518)
leynos Aug 19, 2026
66db92d
Exercise Windows dyndep retention through real pruning (#518)
leynos Aug 19, 2026
c005f1f
Repair rebased discovery contracts (#518)
leynos Aug 21, 2026
cd54641
Split project layer merge orchestration (#518)
leynos Aug 21, 2026
1674f70
Document fixture path and Ninja file lifecycles
leynos Aug 21, 2026
68e0b5b
Clarify Windows fixture path identity guidance
leynos Aug 21, 2026
33fb533
Use standard spelling in path identity guidance
leynos Aug 21, 2026
037824c
Validate fixture paths and coverage upload (#518)
leynos Aug 21, 2026
28033cb
Use standard spellings in developer guidance
leynos Aug 21, 2026
8726074
Avoid tracing absent project layers (#518)
leynos Aug 21, 2026
b9e3c0c
Document deferred discovery diagnostics
leynos Aug 22, 2026
9c16ea3
Defer project-layer diagnostics (#518)
leynos Aug 22, 2026
da68b00
Remove review triage from CI guide (#518)
leynos Aug 22, 2026
e7fefd4
Use UTF-8 paths in stdlib assertions (#518)
leynos Aug 22, 2026
9b1978a
Correct discovery ExecPlan evidence (#518)
leynos Aug 22, 2026
5f9a5a7
Repair rebase integration (#518)
leynos Aug 22, 2026
6c74639
Harden canonicalization test contracts (#518)
leynos Aug 22, 2026
d9e6fcb
Use Windows exit-status type directly (#518)
leynos Aug 22, 2026
bb4112d
Document discovery trace boundary (#518)
leynos Aug 22, 2026
61170dc
Match Ninja argument test canonicalization (#518)
leynos Aug 23, 2026
0b8d476
Repair rebased discovery integration (#518)
leynos Aug 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 108 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ on:
types: [opened, synchronize, reopened]
workflow_dispatch:

env:
# Single source of truth for the cargo-nextest pin. `make test` runs the
# non-doctest suite through nextest, so every job that installs it reads
# this value. Declared once at workflow scope so the documented
# AGENTS.md `sed` extraction yields exactly one version.
NEXTEST_VERSION: '0.9.133'

jobs:
build-test:
runs-on: ubuntu-latest
Expand All @@ -18,9 +25,6 @@ jobs:
# the dated nightly pinned in rust-toolchain.toml.
NETSUKE_RUST_TOOLCHAIN: nightly-2026-06-25
WHITAKER_INSTALLER_VERSION: '0.2.7'
# Single source of truth for the cargo-nextest pin. `make test` runs the
# non-doctest suite through nextest, so the job installs it up front.
NEXTEST_VERSION: '0.9.133'
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
Expand Down Expand Up @@ -48,7 +52,6 @@ jobs:
uses: leynos/shared-actions/.github/actions/setup-rust@8add2d99854a5b77548eae98cca59202e68fefc8
with:
toolchain: ${{ env.NETSUKE_RUST_TOOLCHAIN }}
components: rustfmt, clippy
# Preserve warnings-as-errors and Polonius through toolchain setup.
rustflags: -D warnings -Zpolonius=next
- name: Install cargo-nextest
Expand Down Expand Up @@ -88,7 +91,10 @@ jobs:
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990
with:
python-version: '3.13'
enable-cache: true
# This Rust workspace has no Python dependency manifest. Cache the
# shared spelling dictionary below rather than a uv cache with no
# durable dependency key.
enable-cache: 'false'
- name: Cache shared spelling dictionary
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
Expand Down Expand Up @@ -118,12 +124,105 @@ jobs:
CS_ACCESS_TOKEN: ${{ secrets.CS_ACCESS_TOKEN }}
uses: leynos/shared-actions/.github/actions/upload-codescene-coverage@8add2d99854a5b77548eae98cca59202e68fefc8
with:
path: lcov.info
format: lcov
mode: check
project-url: https://api.codescene.io/v2/projects/69281
access-token: ${{ env.CS_ACCESS_TOKEN }}
installer-checksum: ${{ vars.CODESCENE_CLI_SHA256 }}

build-test-windows:
# Gates merges: the `#[cfg(windows)]` tree is compiled, linted, and tested
# under `-D warnings` on this platform (see #518).
runs-on: windows-latest
permissions:
contents: read
env:
CARGO_TERM_COLOR: always
BUILD_PROFILE: debug
# The tree requires -Zpolonius=next (see
# docs/adr-006-adopt-polonius-nightly-toolchain.md), so CI builds with
# the dated nightly pinned in rust-toolchain.toml.
NETSUKE_RUST_TOOLCHAIN: nightly-2026-06-25
WHITAKER_INSTALLER_VERSION: '0.2.7'
defaults:
run:
# The Makefile uses POSIX shell constructs throughout; Git Bash is
# preinstalled on windows-latest. GNU Make's default recipe shell on
# Windows is cmd.exe, so every make invocation overrides SHELL to bash.
shell: bash
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Comment thread
coderabbitai[bot] marked this conversation as resolved.
with:
persist-credentials: false
- name: Install GNU Make
run: choco install make --yes --no-progress
- name: Setup Rust
uses: leynos/shared-actions/.github/actions/setup-rust@8add2d99854a5b77548eae98cca59202e68fefc8
with:
toolchain: ${{ env.NETSUKE_RUST_TOOLCHAIN }}
# Preserve warnings-as-errors and Polonius through toolchain setup.
rustflags: -D warnings -Zpolonius=next
- name: Install Ninja
uses: seanmiddleditch/gha-setup-ninja@3b1f8f94a2f8254bd26914c4ab9474d4f0015f67 # v6
- name: Install cargo-nextest
uses: taiki-e/install-action@18b1216eba7f8039b0f8d131d5473787f0edce68 # v2.85.3
with:
tool: nextest@${{ env.NEXTEST_VERSION }}
- name: Show rustc version
run: |
rustup show
rustc --version
cargo --version
- name: Show Ninja version
run: ninja --version
- name: Format
run: make SHELL=bash check-fmt
- name: Lint (Clippy)
# Clippy and `cargo doc` over the whole workspace under `-D warnings`,
# including the `#[cfg(windows)]` arms.
run: make SHELL=bash lint-clippy
- name: Cache Whitaker installer
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: |
~/.cargo/bin/whitaker-installer
~/.cache/cargo-binstall
key: whitaker-installer-${{ runner.os }}-${{ runner.arch }}-${{ env.WHITAKER_INSTALLER_VERSION }}
- name: Install Whitaker
# Installs and runs on windows-latest (verified in #562); the same
# binstall-with-cargo-install-fallback path as the Linux job. On
# Windows the installer ships `whitaker` as a PowerShell wrapper
# (.ps1), which Git Bash cannot execute, so shim `whitaker` in the
# cargo bin directory (already on PATH) to run the wrapper through
# PowerShell. A failure here blocks the merge.
run: |
if ! command -v whitaker-installer >/dev/null 2>&1; then
if cargo binstall --version >/dev/null 2>&1; then
cargo binstall --no-confirm --locked "whitaker-installer@${WHITAKER_INSTALLER_VERSION}"
else
echo "cargo-binstall unavailable; building whitaker-installer from crates.io"
cargo install --locked whitaker-installer --version "${WHITAKER_INSTALLER_VERSION}"
fi
fi
whitaker-installer
if [ -f "${HOME}/.local/bin/whitaker.ps1" ]; then
printf '%s\n' \
'#!/bin/bash' \
'exec powershell -NoProfile -ExecutionPolicy Bypass -File "${HOME}/.local/bin/whitaker.ps1" "$@"' \
> "${CARGO_HOME:-$HOME/.cargo}/bin/whitaker"
chmod +x "${CARGO_HOME:-$HOME/.cargo}/bin/whitaker"
fi
- name: Lint (Whitaker)
# Whitaker/Dylint over the workspace under `-D warnings`, including
# the `#[cfg(windows)]` arms. A failure blocks the merge.
run: make SHELL=bash lint-whitaker
- name: Test
# cargo-nextest plus doctests under `-D warnings -Zpolonius=next`,
# compiling and running the `#[cfg(windows)]` test tree. A failure
# blocks the merge.
run: make SHELL=bash test

kani-smoke:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
Expand All @@ -145,6 +244,10 @@ jobs:
toolchain: stable
- name: Install uv
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
with:
# The Kani job has no Python dependency manifest; its explicit cache
# below owns the Rust toolchain artefacts.
enable-cache: false
- name: Cache Kani tools
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/coverage-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ jobs:
# Match rust-toolchain.toml: the tree needs -Zpolonius=next (see
# docs/adr-006-adopt-polonius-nightly-toolchain.md).
toolchain: nightly-2026-06-25
components: rustfmt, clippy
# Preserve warnings-as-errors and Polonius through toolchain setup;
# cargo-llvm-cov appends its instrumentation flags to this value.
rustflags: -D warnings -Zpolonius=next
Expand All @@ -45,6 +44,7 @@ jobs:
if: env.CS_ACCESS_TOKEN != ''
uses: leynos/shared-actions/.github/actions/upload-codescene-coverage@8add2d99854a5b77548eae98cca59202e68fefc8
with:
path: lcov.info
format: lcov
access-token: ${{ env.CS_ACCESS_TOKEN }}
installer-checksum: ${{ vars.CODESCENE_CLI_SHA256 }}
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ cap-primitives = "3.4.4"
cap-std = { version = "3.4.4", features = ["fs_utf8"] }
fs4 = "1.1.0"
camino = "1.2.0"
dunce = "1.0.5"
semver = { version = "1", features = ["serde"] }
anyhow = "1"
indicatif = "0.18.4"
Expand Down
113 changes: 91 additions & 22 deletions docs/developers-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -429,14 +429,15 @@ action's `with.rustflags` input, and none of them may set a job-level
value and silently drop the flag, so the tree would fail to borrow-check with a
confusing `E0499` rather than an obvious configuration error.

Four workflows carry the contract:
Five CI jobs across four workflows carry the contract:

| Workflow | Job | Shared action | `with.rustflags` |
| --------------------------------------------------------------------- | ----------------- | -------------------- | ----------------------------- |
| [`ci.yml`](../.github/workflows/ci.yml) | `build-test` | `setup-rust` | `-D warnings -Zpolonius=next` |
| [`coverage-main.yml`](../.github/workflows/coverage-main.yml) | `coverage-upload` | `setup-rust` | `-D warnings -Zpolonius=next` |
| [`netsukefile-test.yml`](../.github/workflows/netsukefile-test.yml) | `netsukefile` | `setup-rust` | `-Zpolonius=next` |
| [`build-and-package.yml`](../.github/workflows/build-and-package.yml) | `build` | `rust-build-release` | `-Zpolonius=next` |
| Workflow | Job | Shared action | `with.rustflags` |
| --- | --- | --- | --- |
| [`ci.yml`](../.github/workflows/ci.yml) | `build-test` | `setup-rust` | `-D warnings -Zpolonius=next` |
| [`ci.yml`](../.github/workflows/ci.yml) | `build-test-windows` | `setup-rust` | `-D warnings -Zpolonius=next` |
Comment thread
coderabbitai[bot] marked this conversation as resolved.
| [`coverage-main.yml`](../.github/workflows/coverage-main.yml) | `coverage-upload` | `setup-rust` | `-D warnings -Zpolonius=next` |
| [`netsukefile-test.yml`](../.github/workflows/netsukefile-test.yml) | `netsukefile` | `setup-rust` | `-Zpolonius=next` |
| [`build-and-package.yml`](../.github/workflows/build-and-package.yml) | `build` | `rust-build-release` | `-Zpolonius=next` |

CI and coverage add `-D warnings` because those jobs gate on a warning-free
build; the Netsukefile and packaging jobs carry the Polonius flag alone, so a
Expand All @@ -451,7 +452,7 @@ their toolchain through the action's own `toolchain` input and a second,
independently edited pin would let the two disagree.

[`tests/polonius_toolchain_contract.rs`](../tests/polonius_toolchain_contract.rs)
enforces all four callers. For each one it asserts:
enforces all five callers. For each one it asserts:

- the job uses the expected shared-action reference — path *and* pinned
revision, the latter derived from the checked workflows themselves rather
Expand Down Expand Up @@ -1964,6 +1965,19 @@ is not obvious from the name:
regular file with any execute bit set, and `false` for an absent or
unreadable path. It is the inverse of `set_mode`, and exists for probing a
sandbox `PATH` the way an executable lookup would.
- `canonicalize(path: &Utf8Path) -> io::Result<Utf8PathBuf>` is the deliberate
ambient boundary for fixture paths. It delegates to `std::fs::canonicalize`:
`cap_std::fs::Dir::canonicalize` is scoped to a directory handle and returns
a relative path, so it cannot provide the absolute canonical spelling needed
for fixtures in an ambient temporary directory. The helper propagates the
underlying I/O error and returns `io::ErrorKind::InvalidData` when the
canonical path cannot be represented as UTF-8; callers must not hide that
failure with lossy conversion. Because the operation is host-native, tests
that compare native path identity should use this helper, including when
Windows short-name and long-name spellings refer to the same file; identity
follows the filesystem's canonical form rather than handwritten separator
or string normalization. Keep this exception in `test_support::fs`; production
code remains capability-scoped or uses its dedicated normalizer.
- `copy(from, to) -> io::Result<u64>` forwards to `std::fs::copy`, returning
the number of bytes copied and propagating its failure. The `dev_fast`
release fixtures use it to place a built archive under its versioned name.
Expand Down Expand Up @@ -2001,6 +2015,22 @@ claim to model arbitrary scheduler or filesystem interleavings. The fallible
`test_support::fs::inspect_path` probe treats `NotFound` as absence and
propagates every other metadata error.

### Temporary Ninja build files

`runner::process::create_temp_ninja_file` writes, flushes, and synchronizes a
generated Ninja file, then converts the `NamedTempFile` into a
`tempfile::TempPath`. Returning `TempPath` is deliberate: it retains automatic
cleanup while releasing the writer before Ninja reopens the file by path. On
Windows, leaving the original writer open can make Ninja's read fail. Keep the
returned `TempPath` alive until the Ninja invocation completes; dropping it
removes the temporary file.

The regression test
`create_temp_ninja_file_releases_writer_before_external_read` is the lifecycle
contract. It opens the returned path through an independent handle, reads it
back, and checks its contents, length, and `.ninja` suffix. Changes to the
helper must preserve that writer-release and path-lifetime behaviour.

### Shared Makefile contract helpers

`tests/support/makefile.rs` is a shared module for integration tests that
Expand Down Expand Up @@ -2594,8 +2624,12 @@ Configuration merge helpers:
diagnostics for the diagnostic and merge callers.
- `push_discovered_file_layers(composer, errors, discovered) -> ()` transfers
the retained layers and discovery errors into the full merge composition.
- `collect_file_layers_with_trace_and_env_source(directory, env_source)` runs
the one discovery pass and retains bounded project-scope trace metadata.
- `collect_file_layers_with_normalizer_and_trace(directory, normalizer,
env_source)` runs the one discovery pass with the injected path normalizer
and environment source, and retains bounded project-scope trace metadata for
deferred diagnostics. The normalizer canonicalizes comparison keys so
project layers are de-duplicated across equivalent path spellings;
`DiscoveryOutcome::emit_diagnostics()` is the production emission boundary.

- `resolve_json_and_layers_outcome_with_env(cli, matches, env)` retains the
`DiscoveryOutcome` so startup can emit diagnostics after tracing setup and
Expand Down Expand Up @@ -2800,11 +2834,30 @@ signature again. Pinning both is what lets a behavioural test drive `which` and
`tests/stdlib_which_pathext_tests.rs`, which is gated to Windows because
`PATHEXT` governs resolution only there.

That gating has a cost worth stating: CI runs `make test` on `ubuntu-latest`
only, so a `#[cfg(windows)]` test does not gate a merge. Keep host-independent
rules — normalization, the fallback — in the `#[cfg(any(windows, test))]` unit
tests that the Linux suite executes, and reserve the Windows-gated suite for
behaviour that genuinely cannot run elsewhere.
That gating has a cost worth stating: the Windows-gated suite runs only on
`build-test-windows`, so keep host-independent rules — normalization, the
fallback — in the `#[cfg(any(windows, test))]` unit tests that every host
executes, and reserve the Windows-gated suite for behaviour that genuinely
cannot run elsewhere.

The `build-test-windows` job in `.github/workflows/ci.yml` is a merge gate: it
compiles, lints (Clippy and Whitaker), and tests the `#[cfg(windows)]` suite on
`windows-latest` under `-D warnings`, so a Windows-gated test or lint finding
blocks a merge. The split still stands: host-independent rules stay in the
`#[cfg(any(windows, test))]` unit tests so every host — including a developer
on Unix — exercises them, while the Windows-gated suite covers the behaviour
that only exists there.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

The Windows job installs GNU Make through Chocolatey and Ninja through the
setup action, then runs every Make target through Git Bash with `SHELL=bash`.
That override is required because GNU Make otherwise selects `cmd.exe` on
Windows, while Netsuke's recipes use POSIX shell syntax. It installs the
workflow-pinned `cargo-nextest`; the shared Rust setup action supplies
`rustfmt` and Clippy. `whitaker-installer` produces a PowerShell wrapper on
Windows, so the job adds a Bash shim that invokes it through PowerShell before
running `make SHELL=bash lint-whitaker`. To reproduce the platform gate, use a
Windows environment with those tools provisioned and run the four Windows Make
commands from the workflow in that order.

#### `PATHEXT` normalization

Expand All @@ -2829,6 +2882,17 @@ Composition rules:
empty result would mean Windows treats nothing as executable, so `which`
would report every command missing.

The widening was reassessed when `build-test-windows` began compiling and
testing the `#[cfg(windows)]` arm directly (#518): the original motivation for
`#[cfg(any(windows, test))]` — reaching the pure string logic from a CI host
that never compiled Windows — is gone, but reverting to `#[cfg(windows)]`
would drop Unix-host coverage of `parse_pathext`'s normalization,
de-duplication, and fallback rules, which `src/stdlib/which/pathext_tests.rs`
pins on every host. There is no equivalent Unix-side test for a Windows-only
function, so the widening stays: the pure string logic is exercised on both
Linux and Windows, and a Windows-gated regression cannot hide from the Unix
suite.

The full normalization contract, which the property tests in
`src/stdlib/which/pathext_tests.rs` pin:

Expand Down Expand Up @@ -2943,17 +3007,22 @@ split diagnostics, path comparison, and tests out of the main discovery flow:

- `discovery_diagnostics.rs` — bounded tracing helpers (`path_hash`,
`short_hash`, `debug_config_path`, `debug_optional_config_path`,
`warn_explicit_config_load_failed`) and the `ConfigLoadFailureKind` enum used
to classify a load failure without retaining error text.
`debug_project_layer_deduplication`, `warn_explicit_config_load_failed`) and
the `ConfigLoadFailureKind` enum used to classify a load failure without
retaining error text. The de-duplication event records discovered, project,
and appended layer counts after filtering without exposing paths.
- `discovery_paths.rs` — `normalized_path_key` resolves a path to a
comparable, canonicalized form and returns canonicalization errors to its
caller. The discovery-side `comparison_key` fallback uses the original path
literally when resolution fails, continues discovery, and emits only the
normal append debug event. This lets relative or symlinked `--directory`
literally when resolution fails, continues discovery, and emits a bounded
post-filter layer-count event. This lets relative or symlinked `--directory`
values match OrthoConfig's canonicalized layer paths without making an
unresolved path fatal. `FsPathNormalizer` is confined to this comparison
boundary: selectors remain pure path queries, OrthoConfig supplies the layer
path, and tracing remains at the orchestration boundary.
unresolved path fatal. `FsPathNormalizer` uses `dunce::canonicalize` to
mirror OrthoConfig's native Windows identity (without UNC-prefix or
short-name divergence); on other platforms it follows
`std::fs::canonicalize`. Keep it confined to this comparison boundary:
selectors remain pure path queries, OrthoConfig supplies the layer path, and
tracing remains at the orchestration boundary.
- `discovery_event_assertions.rs` — shared test-only helpers:
`capture_events` runs a closure under a TRACE capturing subscriber,
`find_event` locates one emitted event by substring, and `EventAssertion`
Expand Down
Loading
Loading