Sibling document to
dev-flow-implementation-plan.md(merge-time CI gates) andrelease-automation-plan.md(post-merge concerns). This plan owns making the super-strict lint flow actually strict on every target we ship to — originally the strictclippystack only ran natively on macOS and in Docker for Linux; the Windows gate wascargo checkonly (type-check, not lint), and no native macOS → Linux path existed.
All phases now closed:
| Phase | Lands in | Status |
|---|---|---|
| W0 baseline | (in-doc) | ✅ 2026-04-24 |
| W1 xwin clippy recipes | PR #61 | ✅ |
| W2 prod gate | PR #62 (W2–W5 cleanup) | ✅ v0.5.72 |
| W3 test T1 mechanical | PR #62 | ✅ v0.5.72 |
| W4 test T2 targeted | PR #62 | ✅ v0.5.72 |
| W5 test T3 + CI/pre-push flip | PR #138 (this) | ✅ |
W5 follow-on — Tier 2 windows-check removal |
PR #138 (this) | ✅ |
| L1 zigbuild accelerator | PR #138 (this) | ✅ |
| L2 CI parity verification | (deferred; CI Docker image already mirrors rust:latest) |
⏭ |
| P1 plan codification | landed with W0/W1 | ✅ |
The Windows clippy backlog (W0 baseline: 1346 errors on the
authoritative --all-targets -D warnings pass) was driven to zero in
PR #62 (v0.5.72). PR #138 closes the loop by upgrading both gates
that had been left on cargo check:
pr-fast.yml::windows-check(CI) →pr-fast.yml::windows-lintrunningcargo clippy --workspace --all-targets --all-features --locked --no-deps -- -D warningsnatively onwindows-latest.scripts/hooks/_lint_pre_push.sh(local) →just lint-ci-windows(cargo-xwin clippy with the same flag stack), ≈6 s warm.
PR #138 also drops the now-redundant Tier 2 windows-check job
(plan §5 follow-on). Pre-W5.5 it ran cargo check --workspace --all-features --all-targets weekly on windows-latest as the
backstop catching Windows-only regressions before just ship.
With windows-lint now running strict clippy on every PR (which
does a full type-check + executes every dep's build.rs), the
weekly job became strictly redundant and was tombstoned with an
inline comment in tier-2.yml explaining the removal.
And adds the L1 zigbuild accelerator: just lint-ci-linux-zig runs
the full Linux clippy gate natively on macOS via cargo-zigbuild (no
Docker required) in ~50 s cold and sub-second warm. The Docker
lint-ci-linux path remains authoritative; zigbuild is a
developer-loop accelerator only.
Two non-obvious wrinkles surfaced during L1.3 empirical verification on this workspace and are now baked into the recipe + install path:
-
Zig version pin (0.14.1). Homebrew's
zigformula tracks latest (currently 0.16.x) which has unrelated incompat issues withpsm'ssrc/arch/x86_64.sATT-syntax assembly. zig 0.14.1 compilespsmcleanly (and works around a separateblake3dialect-detection issue when paired with the rustflags override below).just install-dev-toolstherefore downloads the official ziglang.org tarball into~/.local/zig/0.14.1/and symlinks it into~/.cargo/bin/zigso it shadows anybrew install zig. -
target-cpu=nativerustflags override..cargo/config.tomlsetsrustflags = ["-C", "target-cpu=native", …]for the Linux target so native Linux builds get host-tuned codegen. On a macOS host that resolves toapple-m4, whichcargo-zigbuildpropagates tozig cc -mcpu=nativealongside-target x86_64-linux-gnu. The combined flag set corrupts zig's integrated-assembler dialect detection, producing thousands ofunrecognized instruction mnemonicerrors when compiling the x86_64 SIMD hand-written assembly that ships inpsmandblake3. The recipe overridesCARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGSto pintarget-cpu=x86-64-v3(the same baselinerelease.yml's Linux matrix uses) for the cross-compile only — native Linux builds via the Docker path are unaffected.
| Aspect | Today | Target |
|---|---|---|
| Strict clippy flag stack | prod_flags + test_flags + -D warnings (see @/Users/rnio/Private/Github/UltraFastFileSearch/just/shared.just:28-30) |
Unchanged — zero flag churn; only the surface it runs on changes |
| macOS host gate | lint-prod + lint-tests + lint-ci — strict ✅ |
Unchanged |
| Linux-in-Docker gate | lint-ci-linux — -D warnings ✅ |
Unchanged (stays the authoritative Linux gate) |
| Linux native from macOS | Blocked (zstd-sys build.rs needs a C cross-compiler Apple cc can't provide) → ✅ cargo-zigbuild accelerator now wired (just lint-ci-linux-zig) |
Done — handles C deps via zig cc; Docker path stays authoritative |
| Windows local (macOS → Win) | cargo xwin check only (type-check, no lint) → ✅ pre-push now runs just lint-ci-windows (cargo xwin clippy -- -D warnings) |
Done — same three-pass flag stack |
| Windows CI | cargo check on windows-latest → ✅ cargo clippy -- -D warnings on windows-latest (pr-fast.yml::windows-lint) |
Done |
Windows clippy backlog (prod: --lib --bins) |
93 errors (measured W0, 2026-04-24) | 0 after Phase W2 |
Windows clippy backlog (tests: --tests) |
1,342 errors (measured W0) | 0 after Phases W3 – W5 |
Windows clippy backlog (ci: --all-targets -D warnings) |
1,346 errors (measured W0) | 0 after W5 |
Blanket #[allow] added |
— | 0 (target for #[expect(lint, reason)] < 20 total, mostly cast-family FFI sites) |
| Gate integration model | 4-layer (IDE / pre-commit / pre-push / PR CI) | Unchanged model; two new authoritative gates added at PR CI, pre-push budget preserved |
Central thesis:
Every line we ship to every target we support must be held to the same clippy stack. A lint gate that only covers the host platform is not a strict lint gate — it is a host-platform lint gate.
- G1: Converge the Windows clippy backlog (baseline TBD in W0.3) to zero without a single blanket
#[allow]. The only acceptable suppression is#[expect(lint, reason = "prose")]on an individual item, and the target count for those is < 10 across the whole Windows surface. - G2: Upgrade
check-windows(local) andwindows-check(PR CI) fromcargo check→cargo clippywith the full three-pass strict flag stack, then gate on it via the existingrequiredaggregator. - G3: Add a native macOS → Linux cross-check path via
cargo-zigbuildthat compileszstd-sys(and any other C-dep crates) without Docker, giving developers a fast local Linux sweep. - G4: Keep the existing
lint-ci-linuxDocker path as the authoritative Linux gate (it mirrors the CI image exactly). Zigbuild is a developer accelerator, not a replacement. - G5: Document per-phase acceptance criteria, escalation policy, and the "new Windows code must be lint-clean from day 1" rule.
- NG1: Running
cargo xwin clippyin pre-commit. Too expensive (~20–60 s warm) for the T1 sub-2-s budget. Stays at pre-push + CI. - NG2: Changing the clippy flag stack itself.
common_flags/prod_flags/test_flagsat@/Users/rnio/Private/Github/UltraFastFileSearch/just/shared.just:28-30stay exactly as defined. Any flag tweak is a separate PR. - NG3: ARM64 cross-check (macOS ARM, Linux ARM, Windows ARM). The workspace only ships x86_64 today; ARM is out of scope until AFTER the amd64 cleanup lands.
- NG4: Cross-target
miri/cargo-udeps/ coverage. Those stay single-target in Tier 2 nightly. - NG5: Any behavioural change to Windows code beyond what a specific clippy rule requires. Every fix is surgical; no drive-by refactors.
- NG6: Replacing Docker with zigbuild as the authoritative Linux gate. Zigbuild uses a different C compiler than the CI's glibc; keeping Docker as authoritative eliminates that drift risk.
Current local gate (@/Users/rnio/Private/Github/UltraFastFileSearch/just/dev.just:52-55):
cargo xwin check --workspace --all-targets --all-features --target x86_64-pc-windows-msvcCatches type errors, missing imports, wrong cfg — not clippy lints.
Current CI gate (@/Users/rnio/Private/Github/UltraFastFileSearch/.github/workflows/pr-fast.yml:436-458):
windows-check:
name: Windows compile check
runs-on: windows-latest
needs: [classify, sanity]
if: needs.classify.outputs.code == 'true'
timeout-minutes: 25
steps:
- run: cargo check --workspace --all-targets --all-features --lockedAlso check-only. Native, so it catches real Windows compile issues (cfg mismatches, missing windows crate feature activations), but not strict lints.
Scope of Windows-only code: 346 #[cfg(windows)] / cfg(target_os = "windows") matches across 88 files. Heaviest files:
@/Users/rnio/Private/Github/UltraFastFileSearch/crates/uffs-mft/src/io/readers/mod.rs(21 matches)@/Users/rnio/Private/Github/UltraFastFileSearch/crates/uffs-broker/src/broker.rs(19 matches)@/Users/rnio/Private/Github/UltraFastFileSearch/crates/uffs-mft/src/platform/system.rs(18 matches)@/Users/rnio/Private/Github/UltraFastFileSearch/crates/uffs-mft/src/io/readers/parallel/mod.rs(12 matches)@/Users/rnio/Private/Github/UltraFastFileSearch/crates/uffs-mft/src/reader/index_read.rs(12 matches)- All of
@/Users/rnio/Private/Github/UltraFastFileSearch/crates/uffs-mft/src/commands/windows/(6 files)
zstd-syscompiles a bundled C library viabuild.rsusingcc-rs.- From macOS, Apple's
cccannot produce Linux x86_64 ELF objects without an explicit cross-compiler. - Today this is bypassed by running the Linux lint pass inside Docker (
@/Users/rnio/Private/Github/UltraFastFileSearch/just/test.just:82-107). It works but:- Requires a Docker runtime (Docker Desktop / colima / OrbStack / Podman).
- Cold first-run is slow (~5–10 min to fetch the
rust:latestimage and compile from scratch). - Subsequent runs use a Docker volume cache (
uffs-linux-target) — warm runs are ~60–90 s. - Blocks the
check-all-targetsrecipe for anyone without Docker installed.
2.3 The hidden third gap (worth calling out)
Native CI Windows clippy has never been gated either. Fixing Item A locally but leaving CI on cargo check leaves us one git push --no-verify away from drift. This plan treats local + CI as a single unit — both upgrade in Phase W4.
| Option | Handles zstd-sys? |
Effort | Speed (warm) | Developer UX | Verdict |
|---|---|---|---|---|---|
A — Keep Docker-only (lint-ci-linux as-is) |
✓ | 0 | 60–90 s | Requires Docker runtime | Keep as authoritative fallback |
B — cross (cross-rs/cross) |
✓ (uses Docker under the hood) | Low | Similar to Docker | Simpler CLI than raw Docker | Strictly worse than A (still Docker) |
C — Install x86_64-linux-musl C toolchain via Homebrew (musl-cross formula + CC_x86_64_unknown_linux_musl env) |
Partial | Medium | Fast | Brittle; musl ≠ glibc (distinct lint profile, distinct platform cfgs) | Reject (produces musl, not the gnu target our CI uses) |
D — cargo-zigbuild |
✓ (zig cc handles glibc + libc cross, including C deps) | Low | ~10–15 s overhead vs native | brew install zig && cargo install cargo-zigbuild; no Docker needed |
Adopt as accelerator |
| E — Defer (accept no native option) | — | 0 | — | Docker required forever | Reject — gap stays open |
Chosen: A + D in parallel. Keep Docker as the authoritative Linux gate (mirrors CI image exactly; runs rust:latest); add zigbuild as an optional developer accelerator for fast inner-loop Linux sweeps on macOS.
Why zig cc works where Apple's cc doesn't: Zig ships a hermetic, multi-platform C compiler with every supported libc bundled. cargo zigbuild --target x86_64-unknown-linux-gnu sets CC_x86_64_unknown_linux_gnu=zig cc -target x86_64-linux-gnu, which cc-rs (the crate that drives zstd-sys's build.rs) respects. No external toolchain install beyond brew install zig + cargo install cargo-zigbuild.
- W0.1 — Run
cargo xwin clippy --workspace --lib --bins --all-features --target x86_64-pc-windows-msvc --no-deps -- {{ prod_flags }}and capture full output to_trash/w0-baseline/prod.txt(gitignored). - W0.2 — Same with
--tests+test_flags. - W0.3 — Same with
--all-targets+-D warnings. This is the count that matches thewindows-checkCI job's authoritative scope. - W0.4 — Bucket lints by rule (
awk -F'' '/warning: .*.*/ {print $2}' | sort | uniq -c | sort -rn`) and fill the table below. - W0.5 — Tier each rule into T1 / T2 / T3 per § 4.1. Tiers are rescoped if W0.3 reveals a rule we haven't seen before.
Baseline results (measured 2026-04-24 on chore/misc-fixes-2026-04-24 @ a83f3543e, x86_64-pc-windows-msvc, rustc 1.96.0-nightly):
| Pass | Scope | Flag stack | Errors | Warm duration |
|---|---|---|---|---|
| prod | --lib --bins |
prod_flags |
93 | 4 s |
| tests | --tests |
test_flags |
1,342 | 6 s |
| ci | --all-targets |
-D warnings |
1,346 | 7 s |
Architectural signal: Windows-only production code is nearly lint-clean (93 errors, ~15 rule families — mostly FFI-safety annotations and pedantic nits). The backlog lives in test + bench code (1,342). This is a significant re-shaping of the plan: we can land the prod gate in a week, then chip away at the test tail independently.
Top rule distribution (union across all three passes; showing top 30 of ~60 unique rules):
| Lint rule | Total | In prod? | Tier | Fix shape |
|---|---|---|---|---|
clippy::cast_possible_truncation |
278 | no | T2 | as u32 → u32::try_from(…)? |
clippy::min_ident_chars |
241 | yes (9) | T1 | rename i / x to descriptive (check clippy.toml config) |
clippy::indexing_slicing |
188 | yes (2) | T2 | a[i] → .get(i).ok_or(…)? or justify #[expect] |
clippy::borrow_as_ptr |
177 | yes (9) | T1 | &x as *const _ → std::ptr::from_ref(&x) |
clippy::unseparated_literal_suffix |
136 | yes (12) | T1 | 1u32 → 1_u32 |
clippy::std_instead_of_core |
124 | no | T1 | std::mem::size_of → core::mem::size_of |
clippy::doc_markdown |
95 | no | T1 | backtick bare identifiers in docs |
clippy::ref_patterns |
83 | no | T3 | ref x @ → &x destructure (pattern semantics) |
clippy::default_numeric_fallback |
74 | no | T1 | let x = 0 → let x: u32 = 0 |
clippy::undocumented_unsafe_blocks |
72 | yes (10) | T2 | write prose SAFETY: comments (FFI justification) |
clippy::missing_docs_in_private_items |
70 | yes (3) | T1 | add rustdoc |
clippy::missing_errors_doc |
59 | no | T1 | add # Errors rustdoc section |
clippy::semicolon_outside_block |
57 | no | T1 | { …; } → { … }; |
clippy::shadow_reuse |
55 | no | T2 | rename shadowed binding |
clippy::cognitive_complexity |
54 | yes (2) | T3 | structural split of complex fns |
clippy::cast_possible_wrap |
48 | no | T2 | signed-wrap concern, per-site |
clippy::option_if_let_else |
46 | no | T2 | reshape Option handling |
clippy::let_underscore_untyped |
44 | yes (8) | T1 | annotate type |
clippy::let_underscore_must_use |
44 | yes (8) | T1 | drop(x) or handle result |
clippy::wildcard_imports |
40 | no | T1 | expand use foo::* |
clippy::items_after_statements |
40 | no | T1 | hoist fn above first stmt |
clippy::as_pointer_underscore |
39 | no | T2 | annotate target pointer type |
clippy::shadow_unrelated |
38 | no | T2 | rename |
clippy::cast_sign_loss |
38 | no | T2 | per-site cast family |
clippy::cast_precision_loss |
38 | no | T2 | per-site cast family |
clippy::too_many_lines |
36 | no | T3 | split fns (threshold=150 in clippy.toml) |
clippy::multiple_unsafe_ops_per_block |
35 | yes (5) | T3 | split unsafe blocks with prose each |
clippy::manual_checked_ops |
34 | no | T1 | use checked_* API |
clippy::float_arithmetic |
32 | no | T3 | justify each use (often legit for perf) |
clippy::collapsible_if |
28 | yes (2) | T1 | merge nested if |
| top 30 total | ~2,076 | 70 | — | ~88 % of the 1,346 authoritative CI count (union counts > individual because many rules fire across multiple passes) |
Full per-rule and per-file distribution is captured in _trash/w0-baseline/ (gitignored, not checked in).
Tier split for the 93 prod lints (the workstream we start with):
- T1 (mechanical, ~70 of 93):
unseparated_literal_suffix(12),borrow_as_ptr(9),min_ident_chars(9),let_underscore_untyped(8),let_underscore_must_use(8),trivially_copy_pass_by_ref(6),print_stderr(4),missing_docs_in_private_items(3),indexing_slicing(2),collapsible_if(2),cognitive_complexity(2),print_stdout(2),uninlined_format_args(1) - T2 (targeted, ~15 of 93):
undocumented_unsafe_blocks(10),indexing_slicing(2),cognitive_complexity(2) - T3 (semantic, ~5 of 93):
multiple_unsafe_ops_per_block(5)
Acceptance (W0 completion): per-rule breakdown table populated above. Next: execute Phase W1 (infrastructure) then W2 (prod cleanup).
- T1 (mechanical): deterministic edits. Rename, add
#[must_use], add rustdoc# Errors/# Panicssections. Low risk; the lint tells you exactly what to do. - T2 (targeted): per-site reasoning required but change is local. Cast conversions (
as u32→u32::try_from), closure simplifications, scope tightening, iterator method choice. - T3 (semantic): touches design surface.
significant_drop_tightening,significant_drop_in_scrutinee,await_holding_lock— these expose real concurrency hazards and each fix needs a regression test.
-
W1.1 — Add three new recipes in
@/Users/rnio/Private/Github/UltraFastFileSearch/just/test.just(sibling tolint-prod/lint-tests/lint-ci):# Ultra-strict Windows production lint (cargo-xwin). Mirrors lint-prod. lint-prod-windows: cargo xwin clippy --workspace --lib --bins --all-features --target x86_64-pc-windows-msvc --no-deps -- {{ prod_flags }} lint-tests-windows: cargo xwin clippy --workspace --tests --all-features --target x86_64-pc-windows-msvc --no-deps -- {{ test_flags }} lint-ci-windows: cargo xwin clippy --workspace --all-targets --all-features --target x86_64-pc-windows-msvc --no-deps -- -D warnings
-
W1.2 — Update
lint-all+check-all-targetsto call the new recipes. -
W1.3 — Decide naming: either rename
check-windows→lint-windows(canonical) with a deprecation redirect, or keepcheck-windowsas the fast type-check entry-point and makelint-ci-windowsthe strict entry. Default: keep both;check-windowsstays as a cheap fallback. -
W1.4 — Measure warm xwin clippy duration for each pass; record in this doc's § 5 table below.
Acceptance (2026-04-24, ✅ met): just lint-prod-windows + just lint-tests-windows + just lint-ci-windows run and report lint counts matching the W0.3 baseline (93 / 1,342 / 1,346 ±1 — the ±1 offset is the error: could not compile summary line cargo wraps each aborted crate in). No semantic change. Raw outputs captured in _trash/w1-measurements/ (gitignored).
The W0 baseline reveals prod is only 93 errors across ~15 rule families — an order of magnitude smaller than the test backlog. Fixing prod first gives us an early win: just lint-prod-windows becomes a green gate that blocks new Windows-gated production regressions while W3 – W5 chip away at the existing test backlog.
- W2.1 — T1 mechanical (~70 of the 93): one PR per crate. Conventional commits by scope:
style(broker): unseparated_literal_suffix + borrow_as_ptr cleanuprefactor(mft): let_underscore_must_use + trivially_copy_pass_by_refdocs(daemon): missing_docs_in_private_items on FFI bindings- etc.
- W2.2 — T2 targeted (~15 of 93):
undocumented_unsafe_blocks(10) needs proseSAFETY:comments justifying eachunsafeblock's invariants (FFI signature, buffer lifetime, alignment — usually all three).indexing_slicing(2) andcognitive_complexity(2) per-site. - W2.3 — T3 semantic (~5 of 93):
multiple_unsafe_ops_per_block(5) splitsunsafe { read(); transmute(); }into discreteunsafe {}blocks, each with its own SAFETY comment. Touches FFI design surface; regression tests for each split. - W2.4 — Wire
lint-prod-windowsinto pre-push and PR CI. This is the first concrete authoritative-gate upgrade: new Windows production code is now lint-clean from day one. W1.4 measurement shows prod pass is 2 s warm — pre-push bundle gains negligible time (current 50 s bundle → ~52 s). PR CI job adds a sub-minute step.
Acceptance after W2: just lint-prod-windows exits 0. PR Fast CI runs Windows clippy on --lib --bins. Production Windows regressions are now authoritatively gated. Test backlog (~1,249 remaining) is UNBLOCKED and parallelisable.
~900 of the remaining ~1,249 test lints are mechanical. Phased by rule family, not by crate, because the rule-level PR is smaller and reviewable.
- W3.1 —
min_ident_chars(241 total, mostly tests): inspectclippy.tomlfor themin-ident-chars-thresholdsetting. If tests legitimately usei/xin tight loops, we relax inclippy.toml(workspace-wide knob, reviewed), not via#[expect]spam. Otherwise rename — 241 cases are tedious but mechanical. - W3.2 —
cast_possible_truncationin tests (139): mostlylen as u32style.u32::try_from(len).expect("test fixture fits in u32")is the idiom; theexpectis justified because tests author-controls the inputs. - W3.3 —
borrow_as_ptr(168 in tests):&x as *const _→std::ptr::from_ref(&x)or::from_mut(&mut x). Deterministic. - W3.4 —
unseparated_literal_suffix(124 in tests):1u32→1_u32.sed-safe at the file level. - W3.5 —
std_instead_of_core(124): workspace-wide decision — do we actually wantcorepreference or is this a false-positive? If we want it, mechanical fix; if we don't, relax in workspace.lints. - W3.6 —
doc_markdown(95): backtick bare identifiers. Mechanical. - W3.7 —
default_numeric_fallback(74),missing_docs_in_private_items(67 test),missing_errors_doc(59),semicolon_outside_block(57),let_underscore_*(80),wildcard_imports(40),items_after_statements(40),uninlined_format_args(21),collapsible_if(26 test), etc. One PR per rule family or per natural grouping.
Acceptance after W3: ≥ 65 % of remaining test backlog cleared. Only T2 + T3 categories left (~350 lints).
- W4.1 —
indexing_slicing(186 in tests): everya[i]in test code is either (a) refactored to.get(i).ok_or_else(|| …)if the index may be bogus, or (b) wrapped in#[expect(clippy::indexing_slicing, reason = "test fixture guarantees index is in range")]with the invariant stated. Per-site reasoning; no blanket allow. - W4.2 —
cast_possible_wrap(48),cast_sign_loss(38),cast_precision_loss(38),as_pointer_underscore(39),ptr_as_ptr(17),cast_lossless(16),unnecessary_cast(12): thecast_*family is where real bugs hide. EveryasbecomesTryFrom::try_fromor a motivated#[expect]with an explicit invariant. Each fix that changes a returned type gets a test update. - W4.3 —
option_if_let_else(46),shadow_reuse(55),shadow_unrelated(38),needless_pass_by_value(12),map_unwrap_or(14),manual_let_else(14),single_match_else(16),wildcard_enum_match_arm(16),trivially_copy_pass_by_ref(12 test),missing_const_for_fn(18): mechanical-with-reading. - W4.4 —
undocumented_unsafe_blocks(62 in tests): write proseSAFETY:comments. Unlike prod, test SAFETY comments can be briefer ("test fixture; no concurrent access") but must still be present.
Acceptance after W4: ≥ 90 % of backlog cleared; only T3 remains (~150 lints).
- W5.1 —
cognitive_complexity(52 in tests, threshold=30 inclippy.toml): many tests are inherently sequential setup → action → assertions. Fix path: extract helper fns, move setup to fixtures, split multi-assertion tests. Each extraction is reviewed — this is where test clarity improves most. - W5.2 —
too_many_lines(36, threshold=150): same play as cognitive_complexity — extract helpers, split assertions into separate#[test]fns. - W5.3 —
ref_patterns(83):if let Some(ref x @ Foo(_)) = …→ explicit borrow destructure. Pattern-semantics sensitive; each fix reviewed. - W5.4 —
multiple_unsafe_ops_per_block(30 in tests),float_arithmetic(32): per-site motivation. Float arithmetic in perf benches is legitimate; add#[expect]with reason. - W5.5 — Upgrade
windows-checkin PR Fast CI fromcargo check→cargo clippy --workspace --all-targets --all-features --locked --no-deps -- -D warnings. Rename jobwindows-check→windows-lintfor clarity. Keepwindows-latestrunner. - W5.6 — Upgrade local
check-windowsin pre-push tolint-ci-windows. W1.4 measurement shows CI pass is 6 s warm — pre-push bundle lands well under the 60 s target (current 50 s → ~56 s). The original "if > 90 s, back off" budget gate is no longer needed; proceed directly to full-scope pre-push. - W5.7 — Final
#[expect(…, reason = …)]audit. Target: fewer than 20 total across the whole Windows surface (higher than the original 10 to accommodate cast-family FFI sites); each with prosereason.
Acceptance after W5: just lint-ci-windows exits 0. PR Fast CI windows-lint runs clippy and passes. required aggregator gates on it. No code can merge without passing Windows clippy on all targets.
-
L1.1 — Document
brew install zig+cargo install cargo-zigbuildin CONTRIBUTING.md and add toinstall-dev-toolsrecipe (@/Users/rnio/Private/Github/UltraFastFileSearch/just/test.just:152-186). -
L1.2 — Add recipe in
just/test.just:# Native macOS → Linux clippy sweep via cargo-zigbuild. # Faster inner-loop alternative to `lint-ci-linux` (Docker). # Docker path remains authoritative — this is a dev accelerator. lint-ci-linux-zig: @printf "\033[0;34m🦎 Linux x86_64 clippy via cargo-zigbuild (native)...\033[0m\n" CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER="zig cc -target x86_64-linux-gnu" \ CC_x86_64_unknown_linux_gnu="zig cc -target x86_64-linux-gnu" \ cargo clippy --workspace --all-targets --all-features --target x86_64-unknown-linux-gnu --no-deps -- -D warnings
(Exact env-var form validated during L1.3;
cargo-zigbuildmay also be invoked directly as a wrapper —cargo zigbuild clippy— whichever pattern proves to drivecc-rscorrectly forzstd-sys.) -
L1.3 — Prove
zstd-syscompiles green via zigbuild. If any other C-dep crate (e.g.ring,openssl-sys) trips, document and add explicitCC_x86_64_unknown_linux_gnuenv overrides in the recipe. -
L1.4 — Wire into
check-all-targets(just/dev.just:66-88) as an optional faster path whenzigis onPATH; Docker path still runs whenzigis absent.
Acceptance: just lint-ci-linux-zig runs green on any macOS dev box with zig installed, in ≤ 30 s warm. Docker path unchanged.
- L2.1 — Confirm
lint-ci-linux(Docker) output matchespr-fast.yml'sclippyjob output exactly (same lints fire / don't fire). If it drifts, pin therust:latesttag to the same rustc the CI runner uses (source:@/Users/rnio/Private/Github/UltraFastFileSearch/rust-toolchain.toml).
Acceptance: a red PR's Docker run produces the same failing lints as the GitHub Actions run.
- P1.1 — This doc lands on
mainwith the W0 baseline table filled in. - P1.2 — Cross-reference from
@/Users/rnio/Private/Github/UltraFastFileSearch/docs/architecture/dev-flow-implementation-plan.md§ 1.3 (gates) and@/Users/rnio/Private/Github/UltraFastFileSearch/docs/architecture/release-automation-plan.md§ 1 (non-goals list). - P1.3 — Update
@/Users/rnio/Private/Github/UltraFastFileSearch/CONTRIBUTING.md"Architecture guardrails" to include "new Windows-gated code must passjust lint-ci-windowsbefore PR".
| Layer | Current | After W4 + L1 |
|---|---|---|
| IDE save | rust-analyzer check-on-save (native only) | Unchanged (cross-target too expensive for on-save) |
pre-commit (lint-fast, sub-2 s / ~25 s Rust) |
fmt + native clippy trio + typos + reuse + file-size | Unchanged — xwin clippy too expensive here |
pre-push (lint-pre-push, ≤ 60 s target) |
fmt + native trio + rustdoc + doc-tests + tests + smoke + check-windows (8 s check) |
Upgrade: check-windows → lint-ci-windows (+ 6 s warm, measured W1.4). Bundle goes 50 s → ≈56 s — well under target. Original W4.4 "> 90 s back off" gate no longer needed. |
PR CI (pr-fast.yml) |
Required: classify + file-size + fmt + sanity + clippy + docs + test-build + tests + security + windows-check (check) |
Upgrade windows-check → windows-lint running cargo clippy -- -D warnings. Linux clippy already covered by the existing clippy job on ubuntu-22.04 (no change needed for Linux). |
Tier 2 nightly (tier-2.yml) |
Weekly windows-latest cargo check + coverage + udeps + miri |
Drop the now-redundant windows check (PR-time authoritative post-W4.3); keep coverage / udeps / miri single-target. |
Flag stack discipline: all three targets (native host, Linux, Windows) use the exact same common_flags / prod_flags / test_flags from @/Users/rnio/Private/Github/UltraFastFileSearch/just/shared.just:28-30. Zero target-specific allowlists. Any rule that truly diverges per-target (e.g. cast_possible_wrap on 32-bit targets) goes in [workspace.lints] in the root Cargo.toml where it is visible and review-gated.
xwin clippy warm-runtime measurements (filled by W1.4, 2026-04-24):
| Pass | Command | Warm duration | Lint count (matches W0) |
|---|---|---|---|
| prod | just lint-prod-windows |
2 s | 93 ±1 ✅ |
| tests | just lint-tests-windows |
6 s | 1,342 ±1 ✅ |
| ci | just lint-ci-windows |
6 s | 1,346 ±1 ✅ |
Pre-push budget implication: all three passes fit well under the 60 s pre-push budget, even full-workspace (lint-ci-windows at 6 s warm). The W5.6 "budget gate" conservatism can now be relaxed — there is room to run either lint-prod-windows (W2.4) or even lint-ci-windows (W5.6) in pre-push without exceeding 60 s. Decision now safe to make at W2.4 empirically, not at W5.6.
| Rule | How this plan honours it |
|---|---|
| 1. No suppression hacks | Zero blanket #[allow]. Only #[expect(lint::name, reason = "prose")] on individual items, with a target count of < 20 total after W5 (revised up from 10 after W0 revealed the cast-family FFI site density). Every suppression reviewed in the PR that introduces it. |
| 2. Surgical, correct fixes | Phased by lint rule, not by file. Each PR touches one rule family at a time; mechanical T1 fixes are deterministic, T2 + T3 fixes are per-site reasoned. No "refactor-while-I'm-here" smuggling. |
| 3. Preserve behavior & contracts | Public API unchanged except where a lint surfaces a real bug (typically cast_* truncations or significant_drop_* deadlocks). Each API change gets a CHANGELOG entry + regression test. |
| 4. Improve tests, don't dodge | T3 semantic fixes ship with new regression tests (deadlock / ordering / cast-boundary). T1 + T2 fixes rely on the existing Windows test suite remaining green; @/Users/rnio/Private/Github/UltraFastFileSearch/crates/uffs-daemon/tests/ipc_integration.rs + uffs-mft unit tests already exercise the heaviest Windows surface area. |
From the moment Phase W1 lands (xwin clippy recipes exist), all new Windows-gated code must pass just lint-ci-windows before PR. Pre-push will enforce this once W4.4 lands; until then it's a contributor discipline item called out in CONTRIBUTING.md.
- Weekly during W2 – W4: progress snapshot (remaining lint count by rule) posted as a comment on a tracking issue.
- Daily if a tier stalls: swap owner or reclassify the stalled rule into T3 for design review.
If a single lint rule (typically significant_drop_tightening) accumulates > 3 working days of per-site reasoning without progress, the decision is escalated to a design doc — not a blanket #[allow]. Each such rule either gets a small architectural fix (e.g. lock-granularity refactor) or a motivated #[expect] with a linked architectural ticket to remove it.
| Week | Phases | Shape |
|---|---|---|
| 1 | P1 + W0 (✅ done) + W1 + L1 | Codify plan, infrastructure, zigbuild accelerator |
| 1 – 2 | W2 — prod gate (93 lints) | 3 PRs: T1 mechanical, T2 targeted (SAFETY comments), T3 (unsafe-block splits). Ends with lint-prod-windows gating pre-push + CI. |
| 3 | W3 — test T1 mechanical (~900 lints) | One PR per rule family (~10 PRs, each 50–250 sites) |
| 4 – 5 | W4 — test T2 targeted (~350 lints) | One PR per rule family, cast-family gets a dedicated PR |
| 6 | W5 — test T3 semantic + CI upgrade | 2 PRs: semantic fixes, then CI flip |
Total effort: ~6 calendar weeks / ~12–15 focused working days. Prod gate (W2) is the early win — it lands in week 1–2 and provides an authoritative "new Windows code must pass clippy" gate even while the test backlog is still being cleared.
-
just lint-prod-windows+just lint-tests-windows+just lint-ci-windowsexist and are green. (PR #61 / PR #62) -
just lint-ci-linux-zigexists and green; Docker path unchanged and still green. (PR #138 — verified locally on macOS arm64 withzig 0.14.1(pinned, see Status section) +cargo-zigbuild 0.22.3againstx86_64-unknown-linux-gnutarget; ~50 s cold, sub-second warm.) - PR Fast CI
windows-lintjob runscargo clippy -- -D warningsnatively onwindows-latest;requiredaggregator gates on it. (PR #138) - Zero blanket
#[allow]added in this workstream; fewer than 20#[expect(…, reason = …)]across the Windows surface (budget revised from 10 after W0 revealed cast-family FFI site density). (PR #62) - Every
#[expect]has an inline justification comment referencing either the FFI signature it accommodates or an open ticket to remove it. (PR #62) - CONTRIBUTING.md +
dev-flow-implementation-plan.md+release-automation-plan.mdcross-reference this plan. (PR #138 refreshed CONTRIBUTING.md text for the post-W5 state.) - "New Windows code must be lint-clean from day 1" rule enforced at pre-push after W2 (prod) and at PR CI after W5 (full). (PR #138 — pre-push hook now runs
just lint-ci-windows, CI runs strictcargo clippy -- -D warnings.)
- Strict lint flag stack source of truth:
@/Users/rnio/Private/Github/UltraFastFileSearch/just/shared.just:28-30 - Linux Docker lint gate:
@/Users/rnio/Private/Github/UltraFastFileSearch/just/test.just:82-107 - Linux zigbuild lint gate (Phase L1, native macOS → Linux):
@/Users/rnio/Private/Github/UltraFastFileSearch/just/test.just:142-190 - Windows xwin compile-check (local fast path):
@/Users/rnio/Private/Github/UltraFastFileSearch/just/dev.just:171-174 - Windows xwin clippy gate (
lint-ci-windows, mirrors CI's strict pass):@/Users/rnio/Private/Github/UltraFastFileSearch/just/test.just:132-140 - Windows CI gate (
pr-fast.yml::windows-lint, nativewindows-latest):@/Users/rnio/Private/Github/UltraFastFileSearch/.github/workflows/pr-fast.yml:429-467 - Pre-push hook runner (calls
lint-ci-windows):@/Users/rnio/Private/Github/UltraFastFileSearch/scripts/hooks/_lint_pre_push.sh:316-318 - Gate architecture:
@/Users/rnio/Private/Github/UltraFastFileSearch/docs/architecture/dev-flow-implementation-plan.md - Release architecture:
@/Users/rnio/Private/Github/UltraFastFileSearch/docs/architecture/release-automation-plan.md - Cargo-zigbuild upstream: https://github.com/rust-cross/cargo-zigbuild
- Cargo-xwin upstream: https://github.com/rust-cross/cargo-xwin