Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

8 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

WhyCache logo

WhyCache

Stop guessing why Turborepo missed the cache.
Compare two runs, isolate the changed evidence, and find the first task that invalidated the graph.

CI CodeQL RustSec crates.io npm Rust 1.85 or newer License

npm Β· crates.io Β· release binaries Β· issues

Animated WhyCache diagnosis from a real deterministic fixture

WhyCache is a small, fully local Rust CLI for answering one expensive question:

Why did this task miss when I expected a cache hit?

It reads the run summaries Turborepo already produces, compares a known-good baseline with the current run, removes unchanged noise, ranks the remaining causes, and attaches every conclusion to before/after fingerprints. It understands direct invalidation, downstream cascades, and the important case where the hash did not change but the cached artifact was unavailable.

No source upload. No account. No API key. No telemetry. No AI. πŸ›‘οΈ

✨ Why it is useful

A Turborepo task hash can change because of source files, environment fingerprints, lockfiles, global inputs, task configuration, dependency hashes, an upstream task, or even a Turborepo version change. Raw run summaries contain the evidence, but they are large and graph-shaped.

WhyCache turns that evidence into an investigation order:

web#build  MISS  root cause
  Hash      111111111111 β†’ 222222222222

  1. 1 environment fingerprint(s) changed  98% confidence
     β€’ NODE_ENV  env-a β†’ env-b

  2. 1 task input file(s) changed  95% confidence
     β€’ apps/web/src/index.ts  source-a β†’ source-b

  3. 1 upstream task hash(es) changed  90% confidence
     β€’ ui#build  upstream-a β†’ upstream-b

  πŸ’‘ Likely culprit: NODE_ENV changed between runs.

The example above is generated from the checked-in compatibility fixture and protected by exact golden-output tests.

Static WhyCache terminal report showing ranked environment and file evidence

πŸš€ Install

npm β€” recommended

npm install --global whycache

The npm launcher selects the native package for your operating system and architecture. The JavaScript launcher has no third-party runtime libraries.

Cargo

cargo install whycache --locked

Release binary

Download the archive for your platform from GitHub Releases, verify it against SHA256SUMS, and place whycache on your PATH.

Build from source

git clone https://github.com/Lebyy/whycache.git
cd whycache
cargo install --path . --locked

WhyCache requires Rust 1.85 or newer when building from source.

Operating system Architecture npm package Release asset
Linux x86-64 whycache-linux-x64 linux-x64
Linux ARM64 whycache-linux-arm64 linux-arm64
macOS Apple Silicon whycache-darwin-arm64 darwin-arm64
macOS Intel whycache-darwin-x64 darwin-x64
Windows x86-64 whycache-windows-x64 win32-x64

⚑ Quick start

Compare saved Turborepo runs

Ask Turborepo to retain a summary when you run the task:

turbo run build --summarize

After a later run misses, diagnose every missed task:

whycache

Or focus on one task name or fully qualified task id:

whycache build
whycache web#build

By default, WhyCache chooses the newest summary as the current run and the nearest earlier successful summary as the baseline.

Start without saved history

You can run WhyCache before enabling --summarize:

whycache build

WhyCache captures turbo run build --dry=json at .whycache/last-summary.json. A dry summary calculates inputs without executing the task. The first invocation honestly reports baseline_captured; the next invocation compares current inputs against that baseline.

Running whycache without a task reads all task names from turbo.json and captures them together.

Choose the baseline yourself

whycache build --against .turbo/runs/previous.json

External systems can stream a baseline over standard input:

cat previous.json | whycache build --against -

πŸ”Ž What WhyCache compares

Evidence What a change means Reported safely?
Task input fingerprints Source, config, generated input, or declared dependency changed βœ… Path + hash
Environment fingerprints A configured variable affected the task hash βœ… Name + fingerprint only
Lockfiles The external dependency graph changed βœ… Path + hash
turbo.json Repository-wide pipeline configuration changed βœ… Fingerprint
Resolved task definition Command, inputs, outputs, cache policy, or dependencies changed βœ… Structured diff
Global inputs A root-level file or global environment entry invalidated tasks βœ… Path/name + hash
Upstream task hashes A dependency changed first and propagated through the graph βœ… Task id + hash
Package dependency hash External packages or workspace dependency state changed βœ… Hash only
Engine constraints Node/package-manager constraints changed βœ… Constraint text
Turborepo version Hashing behavior may differ between the two runs βœ… Version only
Same hash with a miss The cache key is stable but the artifact was unavailable βœ… Cache metadata

Unchanged evidence is summarized instead of repeated, so a large monorepo report stays focused.

🧭 How to read a diagnosis

WhyCache classifies each task before ranking its evidence:

Classification Meaning First action
root cause The task's own inputs or configuration changed Inspect the highest-confidence evidence
cascade Only an upstream task changed Follow the named upstream task
cache unavailable Hash stayed identical but the run missed Check local/remote cache access and retention
new task The task did not exist in the baseline Confirm the pipeline/package change
unchanged No relevant change was found No cache-key investigation required
unexplained The summary lacks enough supported evidence Review the warning and raw summary version

Confidence is a deterministic priorityβ€”not a probability generated by a model. Exact, task-local evidence ranks above broad or downstream evidence. Multiple plausible causes remain visible.

πŸ“€ Output formats

Human terminal report

whycache build

Color is enabled only for an interactive terminal, so redirected output stays clean.

Stable JSON

whycache build --json > whycache-report.json

JSON output is deterministic for the same inputs and currently uses schema version 1. Consumers should branch on schemaVersion and ignore unknown fields. See the complete JSON contract.

{
  "schemaVersion": "1",
  "tasks": [
    {
      "taskId": "web#build",
      "cacheStatus": "miss",
      "classification": "root_cause",
      "causes": [
        {
          "kind": "environment",
          "confidence": 98
        }
      ]
    }
  ],
  "warnings": []
}

GitHub-flavored Markdown

whycache build --md >> "$GITHUB_STEP_SUMMARY"

Markdown uses the same diagnosis model as human and JSON output, including evidence tables, confidence, likely culprit, unchanged signals, and next checks.

Git line statistics

whycache build --git

When both summaries contain commit SHAs, --git adds line counts for already identified evidence paths. WhyCache invokes the local Git executable with argument arrays; it does not execute a shell command assembled from summary data.

πŸ€– GitHub Actions example

Generate and retain summaries in your build job, then write the diagnosis directly to the step summary:

- name: Install WhyCache
  run: npm install --global whycache

- name: Explain cache misses
  if: always()
  run: whycache build --md --git >> "$GITHUB_STEP_SUMMARY"

For a pull-request bot, store whycache build --json as an artifact and post the Markdown output using your existing trusted workflow permissions.

🧠 How it works

WhyCache discovery, normalization, comparison, diagnosis, and rendering pipeline

  1. Discover β€” walk upward to the Turborepo root and deterministically order .turbo/runs/*.json.
  2. Normalize β€” deserialize known Turbo 1.9 and v2 fields, treat historical null collections as empty, and ignore unknown additions.
  3. Compare β€” diff task-local and global evidence, then build upstream dependency edges.
  4. Diagnose β€” separate direct root causes from inherited cascades and rank causes by specificity.
  5. Explain β€” render one versioned report model as terminal text, JSON, or Markdown.

Renderers never perform diagnosis, which keeps every output format semantically identical. Read the deeper architecture notes.

🧰 CLI reference

Usage: whycache [OPTIONS] [TASK]
Argument Purpose
[TASK] Match a task name such as build or an exact id such as web#build
--against <SUMMARY> Use a specific baseline; pass - to read JSON from stdin
--json Emit stable machine-readable JSON
--md Emit GitHub-flavored Markdown
--git Add Git numstat evidence when both commits are available
-h, --help Show CLI help and examples
-V, --version Show the installed WhyCache version

--json and --md are mutually exclusive. Invalid usage and missing tasks exit with status 2; operational and parse failures exit with status 1.

πŸ” Privacy and security

  • Runs entirely on the local machine or CI runner.
  • Never uploads summaries, source paths, hashes, or Git metadata.
  • Never reads task environment values to reconstruct secrets.
  • Shows environment variable names and Turborepo-provided fingerprints only.
  • Uses no network client, telemetry SDK, analytics, AI, or hosted service.
  • Pins third-party GitHub Actions to immutable commit SHAs.
  • Runs RustSec and CodeQL workflows in addition to the warning-free test matrix.

Please report vulnerabilities privately using the process in SECURITY.md.

πŸ§ͺ Compatibility policy

Surface Coverage
Turborepo 1.9 Real schema-0 fixture and scheduled 1.9.9 canary
Turborepo 2.0 Scheduled 2.0.0 canary
Current Turborepo v2 Real fixture and scheduled latest canary
Linux Native x86-64 and ARM64 release jobs
macOS Native Intel and Apple Silicon release jobs
Windows Native x86-64 release job
Rust MSRV job on Rust 1.85 plus stable CI

Unknown run-summary fields are ignored. Declared schema versions other than 0 and 1 are parsed in compatibility mode with a visible warning; WhyCache does not silently claim full support for an untested schema.

⚠️ Honest limitations

  • WhyCache explains differences present in run summaries; it is not a general-purpose build profiler.
  • A historical miss cannot be reconstructed if no baseline or previous summary exists. Automatic mode starts collecting evidence for the next comparison.
  • An unchanged hash proves the key stayed stable, but a local summary alone cannot distinguish eviction from remote-cache authentication, team selection, or retention policy.
  • Environment values are intentionally unavailable. This protects secrets but means the report names the changed variable rather than printing its plaintext value.
  • If Turborepo introduces a new schema, WhyCache warns until that version has a real fixture and compatibility test.

πŸ› οΈ Development

cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-targets --all-features
cargo +1.85.0 test --all-targets --all-features
cargo audit

The repository is intentionally small:

src/                    CLI, discovery, parsing, diagnosis, Git, renderers
tests/fixtures/         Sanitized real Turbo schemas and deterministic scenarios
tests/golden/           Exact human, JSON, and Markdown output contracts
npm/                    Cross-platform native npm packages and launcher
.github/workflows/      CI, CodeQL, RustSec, canary, and release automation
assets/                 Logo, real terminal capture, and architecture artwork
docs/                   Architecture, JSON contract, and release procedure

Changes must remain warning-free and evidence-backed. Parser changes need a sanitized fixture; diagnosis changes need ranking and classification tests; renderer changes need updated golden files. See CONTRIBUTING.md.

πŸ“¦ Releases

A v* tag builds five native archives, tests each runner, generates checksums, creates the GitHub release, and publishes the matching platform packages, npm installer, and Rust crate. Package versions are synchronized by scripts/sync-version.sh before tagging.

The complete checklist is in docs/releasing.md.

❓ FAQ

Does WhyCache replace turbo run --summarize?

No. Turborepo produces the evidence; WhyCache compares and explains it. When history is empty, WhyCache can invoke Turborepo's dry JSON mode to establish a truthful baseline without executing tasks.

Does it send my repository data to an AI model?

No. There is no AI integration, network client, API key, account, or telemetry path. Diagnosis is a deterministic Rust comparison engine.

Why are confidence scores so exact?

They are stable priorities. A changed environment fingerprint is direct task evidence, an exact file change is slightly lower, and an upstream hash is ranked below local evidence because it describes propagation. They are not statistical probabilities.

Can it run in a large monorepo?

Yes. WhyCache compares summary maps in memory, sorts output deterministically, and collapses unchanged signals. Use a task filter when you want the smallest possible report.

🀝 Contributing

Issues and focused pull requests are welcome. Please read the contribution guide and code of conduct first.

WhyCache is not affiliated with or endorsed by Vercel. Turborepo is a trademark of its respective owner.

πŸ“„ License

Licensed under either the MIT License or Apache License 2.0, at your option.

About

Explain exactly why a Turborepo task missed the cache

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages