Incremental Build Correctness
Did the build system actually rebuild everything this change touched? Incremental builds and remote caches save hours, and silently shipping a stale artifact is the price when the dependency graph they trust is wrong. This sensor compares what the change should have invalidated against what the build did invalidate.
In practice
The reading is a set difference between two lists: every target the change should have invalidated, and every target the build actually rebuilt. When they match, the incremental graph is telling the truth.
change: edit src/render/theme.css
expected: [pages/home.html, pages/about.html, pages/docs.html]
rebuilt: [pages/home.html]
missing: [pages/about.html, pages/docs.html]
verdict: STALE: 2 targets shipped without rebuild
The verdict only exists because of the counterfactual: the same change through a clean build. That is the reference the sensor is compared against, so “clean rebuild on CI matched, incremental on a developer machine did not” is itself a finding, not a coincidence.
The reading habit that matters: when a behavior change ships without a rebuild record, check the graph before checking the code. Stale artifacts masquerade as caching bugs, flaky behavior, and “works on my machine,” and the distinguishing fact is always whether the target that changed state was in the rebuilt set.
Response playbook
When the rebuild set comes up short:
- Reproduce with a clean build. If the clean build fixes the symptom, staleness is confirmed and the graph is the culprit.
- Find the undeclared edge. Walk from the edited file to the stale target; the missing declaration is usually a generated file, a header, or a config read at runtime but not at build time.
- Declare the dependency; do not script around it. A clean build on every CI run papers over the hole and burns the hours the cache was built to save.
- Add the stale target to the regression set so the next undeclared edge fails the same check instead of shipping.
How it gets gamed
- Manual cache busting. Touching files or bumping fake hashes to force a rebuild, instead of fixing the dependency declaration. The graph stays wrong; the workaround becomes load-bearing.
- Clean builds by decree. “Just run clean” as the sanctioned fix converts a correctness bug into a permanent performance tax and hides the sensor entirely.
- Undeclared inputs rationalized. Files read at build time that the manifest never lists, because declaring them was “too invasive.” Every one is a future stale artifact.
The meta-signal is cache-hit correctness: sampled rebuilds whose outputs differ from the cached artifact they replaced.
What it cannot detect
Changes whose semantic effect exceeds their declared dependencies in ways no graph captures (a shared constant edited in a header the graph treats as irrelevant). That class of surprise is what API compatibility and integration tests exist for.
Related sensors
References
Publications
- Reproducible Builds: Increasing the Integrity of Software Supply Chains —
- An Empirical Analysis of Build Failures in the Continuous Integration Workflows of Java-Based Open-Source Software —