Rust build cache
Wardian's Rust launcher reuses one bounded sccache store while giving every worktree a distinct central compiler target. Use the launcher for ordinary checks and tests; a source checkout's running release artifacts are never a compiler target for a worktree.
Protected-input manifests cover both the compiler target and its sibling claim directory. Overlapping protection rejects a claim before either directory is created. Cleanup and owned release recheck protection; a claim that becomes protected remains intact rather than being removed.
npm run rust:check
npm run rust:test
npm run rust:clippy
npm run verify:ci -- --only backend
npm run check:rust-deadcodeThese commands also work in PowerShell. verify:ci --list and frontend/docs-only verification do not acquire a compiler target. Direct cargo retains its normal incremental settings; managed worktree placement is described in the worktree guide.
Public rust:cache -- cargo <arguments> supports build, check, test, clippy, doc, metadata, and fmt. Global verbosity, color, configuration, and offline/locked/frozen flags may precede the command; +toolchain stays first. Unsupported commands and global options are rejected before target claims or process launch. In particular, clean, run, install, and external Cargo subcommands are not forwarded. Use the explicit retention command for owned inactive launcher outputs. Formatting remains available to normal verification.
rust:cache -- inspect reports the exact owner_sha256 and marker_sha256 alongside a claim's parsed owner identity. These hashes bind explicit recovery to the selected worktree generation.
Explicit dependency setup
The dependency is official sccache 0.18.0. Setup never starts a compiler, Cargo build, server, installer for Wardian, or a user runtime. There are two different setup commands:
# Record an already installed official executable; this does not download it.
WARDIAN_SCCACHE='<absolute-sccache-executable>' npm run rust:cache:setupPowerShell:
$env:WARDIAN_SCCACHE = '<absolute-sccache-executable>'
npm run rust:cache:setupOn Windows x64, explicitly download and verify the official release ZIP:
npm run rust:cache:setup -- --installThe installer fetches the ZIP and its SHA-256 sidecar from the official release, verifies the archive before extraction, and stores the executable in a unique private <cache-root>/tools/sccache-0.18.0-<attempt>/ directory. It never changes PATH, replaces an existing executable, or modifies user bins. Attempts and provenance remain inspectable; setup performs no cleanup. On other platforms, obtain the matching official executable yourself and use the first form.
npm run rust:cache -- setup validates and records an available executable. It does not install anything. rust:cache setup --install is rejected; use rust:cache:setup -- --install.
Setup records sccache-identity.json under the cache root: schema, pinned version, absolute wrapper path, executable SHA-256, store path, size limit, and server port. Later launches verify the recorded executable hash. This is an operational record, not an authorization grant or proof of daemon identity.
Layout and concurrency
By default the cache root is the source checkout's sibling <source-checkout>.cargo-cache/. Git's canonical common directory determines the source checkout. Set WARDIAN_RUST_CACHE_ROOT to choose another absolute root. Copied exports and fixtures must supply an explicit WARDIAN_RUST_CACHE_SOURCE_ROOT; they cannot infer a source checkout without Git.
<cache-root>/
sccache/ bounded compiler cache
sccache-identity.json setup record
tools/ explicit dependency setup attempts
launcher-targets/<repo-key>/<tree-key>/ claimed, prunable compiler outputs
deadcode/ visibility-copy compiler outputs
direct-targets/<repo-key>/<tree-key>/ ordinary Cargo outputs, inspect only
claims/<repo-key>/<tree-key>/ exclusive launcher claims
pruning/ explicit prune quarantineOrdinary managed direct Cargo uses direct-targets/. Launcher commands use launcher-targets/ exclusively. A launcher accepts an inherited direct-lane target matching that worktree, then routes its compilation to the launcher lane. Direct outputs never receive launcher ownership or enter its prune set. Do not manually point direct Cargo at a launcher target; that violates the exclusive routing contract and invalidates prune eligibility.
Keys are the first 16 hexadecimal characters of SHA-256 over canonical paths. Only Windows converts backslashes to slashes and folds case. POSIX preserves canonical path bytes, including literal backslashes in filenames. The repository key uses the source checkout; the tree key uses the worktree. Worktrees keep separate targets and claims. The launcher uses Cargo arguments to select its target, removing inherited routing variables only when they agree with that target. Conflicting custom targets fail before compilation; use direct Cargo for an intentionally different target.
The Rust dead-code pass keeps its rewritten source copy under rust-deadcode/<copy-key>/ and compiles it into the separate deadcode/ leaf. Both remain inside the existing exclusive parent claim. Analysis never reuses the normal target's deps, fingerprints or incremental outputs. The leaf is not a separate target generation or claim, and callers cannot select arbitrary descendants. The compiler launcher preserves an explicit target's path spelling for both target and build directories after verifying its physical destination.
withRustCache removes unique Cargo routing environment variables from the parent process. Cargo callbacks should use cargoInvocation; it selects the normal target through arguments, or the closed analysis leaf with output: 'deadcode'. Callbacks that launch Cargo indirectly through Tauri can use callbackCargoEnvironment to set CARGO_TARGET_DIR, CARGO_BUILD_TARGET_DIR and CARGO_BUILD_BUILD_DIR on that child only. It validates the live target marker and claim token, retains wrapper and incremental settings, and rejects conflicting custom routes. Derive artifact lookup from the returned child's target directory. Join every child before the synchronous callback returns and releases its claim.
The existing machine-wide Cargo home continues to share registry archives, unpacked source, and Git dependency caches. No per-worktree Cargo home is created. Cargo has download/mutation locks in its source cache; builds may read the same source cache concurrently. Cargo home and Cargo's cache locking describe the separate source-cache responsibilities.
Sharing a mutable Cargo target would serialize matching profile builds behind Cargo's build-directory lock. Separate targets permit independent compilation; one launcher's target claim prevents two launcher commands writing the same tree. There is no global compiler mutex. Shared sccache storage supports concurrent clients of one server; multiple servers using one local store are unsupported. The managed endpoint defaults to port 4227 and the store cap to 10G. Set SCCACHE_DIR, SCCACHE_CACHE_SIZE, and SCCACHE_SERVER_PORT before explicit setup to record a different arrangement. All clients must agree with the server's actual configuration; do not assume a reused daemon adopted new environment variables. Verify its reported cache location and limits before benchmarking. sccache local storage
Stable Cargo supports build.build-dir since 1.91, including 1.97 and 1.99. The launcher keeps it equal to the isolated target. It does not share dependency output directories or enable nightly fine-grained locking. Splitting final and intermediate directories requires separate compatibility work: Wardian's build script currently derives the ConPTY destination from OUT_DIR ancestry. Cargo changelog
Cache coverage and diagnostics
The launcher selects CARGO_INCREMENTAL=0 only when it injects sccache and no explicit incremental override exists. Uncached commands retain Cargo's default. Explicit environment wrappers, including empty values and workspace wrappers, and wrappers declared in Cargo configuration are preserved rather than replaced. Quoted and dotted wrapper keys, including workspace wrappers, are recognized; extensionless config takes precedence over config.toml. The launcher injects a wrapper only when absence is established in its conservative single-line TOML subset. Includes, inline tables, multiline values, and unfamiliar syntax leave the lane uncached and preserve Cargo's configuration handling. Caller-supplied CLI --config also declines injection, since it can override a wrapper or load another file. Compiler output routing still uses the claimed lane. Set WARDIAN_RUST_CACHE_DISABLE=1 to disable wrapper injection.
For debugging with full symbols and incremental compilation:
CARGO_PROFILE_DEV_DEBUG=2 CARGO_INCREMENTAL=1 npm run rust:checkPowerShell:
$env:CARGO_PROFILE_DEV_DEBUG = '2'
$env:CARGO_INCREMENTAL = '1'
npm run rust:checkExplicit affirmative incremental settings (1 or true), including CARGO_BUILD_INCREMENTAL=true, preserve the override and disable the launcher's sccache injection. An explicit generic carrier is never superseded by inserting CARGO_INCREMENTAL=0. The pinned sccache CLI rejects the numeric opt-in; keeping it injected would fail before compilation. An explicit custom wrapper remains unchanged and is the caller's responsibility. Crates requiring the system linker, including binaries, proc macros and cdylibs, also bypass it. Wardian's combined staticlib,cdylib,rlib app target cannot be counted as a cacheable rlib-only invocation. Build-script execution and generated output remain local; wrapping rustc does not automatically wrap native C/C++ compilation. MSVC is supported, but caching native compilation needs its own compatible compiler invocation.
sccache 0.18.0's Rust source accepts metadata-only rlib compilation, despite its Rust.md still claiming link must be emitted. Treat check/test reuse separately. It hashes compiler identity, source and dependency content, relevant flags, Cargo environment, and compiler cwd. Registry paths are common; local package paths and generated output differ between worktrees. Do not strip semantic paths/environment or assume SCCACHE_BASEDIRS normalizes Rust cache keys. File-reading proc macros require particular care. These are cache eligibility limits, not measured speedups. sccache Rust implementation and Rust caveats
If WARDIAN_PROTECTED_INPUT_MANIFESTS is present, the launcher reports an uncached protected-input lane and never injects sccache, even with an identity record. A reused shared daemon's output roots cannot be qualified by that record. An existing custom wrapper remains subject to the compiler-input guard; there is no exemption or bypass. The isolated target and normal profile still apply. Compiler input protection
Inspect and explicitly prune inactive outputs
npm run rust:cache -- inspect
npm run rust:cache -- prune --keep 2 --max-bytes 10737418240The commands also work in PowerShell. Inspection reports individual targets, bytes, modification times, claims and refusal reasons. Prune retains at most the requested number and byte budget of newest inactive owned targets. It never runs during startup or verification. The sccache store has its own cap; active compiler outputs, tools, and retained failed attempts are additional storage. Central placement alone does not bound total disk usage.
Prune requires the launcher's exact compiler-writable, exclusive-launcher ownership marker and an exclusive target claim. It refuses links, unowned directories, claimed targets, qualified markers and any overlap with protected-input inventories. Direct Cargo outputs and historical shared targets are never pruned. Deletion only addresses the positively claimed launcher generation after renaming it into private quarantine. Claims alone cannot protect a target used by direct Cargo; the separate namespaces are necessary for retention safety.
Crashes leave claims visible. A PID alone is not proof of inactivity; there is no automatic stale-claim repair. An operator or explicitly authorized test controller may archive one claim only after establishing physical closure and supplying its receipt. The command validates the receipt's binding and shape; it cannot independently prove that historical process events occurred.
npm run rust:cache -- recover-ended-claim \
--owner-sha256 <owner-json-sha256> \
--marker-sha256 <target-marker-sha256> \
--closure-receipt <absolute-private-receipt-path>PowerShell:
npm run rust:cache -- recover-ended-claim `
--owner-sha256 <owner-json-sha256> `
--marker-sha256 <target-marker-sha256> `
--closure-receipt <absolute-private-receipt-path>The JSON receipt uses schema 1 and kind rust-cache-ended-claim-recovery. It binds repo_key, worktree_key, both supplied hashes, and the exact owner {token, pid, started}. It includes disposition: "ended", an evidence_producer, an ISO-8601 observed_at, and a closure with complete: true, a root process matching the recorded PID, and every owned descendant. Each process record must state joined: true and closed streams (stdin: "closed", stdout: "eof", stderr: "eof"). The closure basis is either owned-process-handles-joined or owned-job-zero. The Job basis also requires an observed active_process_count: 0; omit Job fields when that count was not observed. An original owned child handle can identify a directly captured test child without presenting its launch timestamp as an OS birth identity. Use captured OS birth identities wherever those identify processes. PID absence, timestamps, expiry, a bare ended assertion, incomplete descendants, unknown generations, or missing joins/stream closure do not qualify.
Recovery refuses changed owner or marker bytes, unknown marker formats, extra claim contents, links, multiply linked metadata, and protected target/claim/ quarantine paths. It moves only the selected claim metadata and exact receipt into claim-recovery/<repo-key>/<worktree-key>/<owner-sha256>; compiler target contents are left in place. A per-worktree reservation blocks new launcher claims and pruning until the archived outcome is durable. If finalization fails after the move, the archive and reservation remain, the command reports partial completion, and repeated commands refuse to mutate that attempt. A successful recovery releases the reservation so the normal launcher can claim the target. The command reports closure_basis from the validated receipt and persists it with the outcome; repeated recovery verifies that field against the archived receipt before returning the prior result.
A qualified or running artifact belongs outside compiler targets in an independently hashed immutable bundle. Pruning never migrates inputs, deletes qualified bundles, or makes a source/release claim.
Evidence required before performance claims
Compare fixed toolchain/source/features under the ordinary dev profile and CARGO_PROFILE_DEV_DEBUG=2, with separate private output roots. Record wall time, commands, flags, exit status and artifact bytes. Distinguish first compile, same-target no-op, and warm shared-cache rebuild into a fresh target. Capture sccache counters and its actual store before and after each run.
Prove concurrent check/test work with overlapping compiler activity in distinct targets and bounded jobs, rather than merely two Cargo processes. Benchmark small fixtures first; serialize tests that share native resources. Repeat representative wardian-core changes before selecting an incremental policy. No document, source inspection, or static Node test establishes a speedup.
npm run rust:cache:testThe default regression suite uses private Node fixtures only. It never invokes Cargo, rustc, native tests, the app, the installer, or a cache server.