Skip to main content

When git cannot answer, rebuild

22 October 2026 · Guardrails & fail-closed · the engineering team

The story

Our publisher decides whether a site needs rebuilding by comparing fingerprints. The fingerprint hashes the engine's files as committed, plus any uncommitted changes to them, and the status command calls a site stale when the latest release holds a different fingerprint. Equal fingerprints are meant to say that a rebuild would produce the same site. Both hashes come from git, and when git failed, each probe returned the string "unknown" in place of a hash. The fingerprint built from those strings came out the same on every call, and the old test asserted exactly that. A release holding it would match the next one.

Git did not have to be missing for this to happen. The probe for uncommitted changes read every untracked path as a file. A nested repository among those paths is a directory, the read raised EISDIR, and the whole value collapsed to unknown.

The fix has two parts. The fingerprint now carries a flag that is false when either probe came back unknown. Status reads stale when the flag is false, and refresh skips its up-to-date shortcut and rebuilds. Separately, the probe now hashes a symlink by its target and a nested repository by the files in it, so those cases produce a real hash. It also reads paths separated by null bytes and diffs without an external diff driver or textconv.

Two tests cover it. In the first, both probes read unknown outside a git repository. A release holding that very fingerprint, with its output folder in place, still reads stale, and refresh rebuilds. With the flag set to true, the same release reads current and refresh reports up to date. The second test builds a nested repository and a directory symlink under the source tree. The dirty value is not unknown, differs from the clean one, and changes when a file inside the nested repository changes.

The pattern

A fingerprint is a claim that two builds would match. A fallback that is a plain string can match itself, so two unknowns compare equal even though neither one says anything about the site. String equality cannot express "I could not tell".

So carry the unknown outside the value. Give the result a separate flag, and let the staleness check and the rebuild shortcut both read it before they compare anything. Unknown then means not current, whatever the stored record holds. A rebuild costs time. A site wrongly called current stays as it was.

Try it

Find the fallback in your own freshness check, the place that catches an error and returns a placeholder. Then:

  1. Return the unknown as its own state, not as a value that can equal another.

  2. Make the staleness check and the skip-the-build shortcut read that state first.

  3. Write a test that stores a record holding the placeholder and asserts the check still reads stale and the build runs.

  4. Write its pair: flip the state to known and assert the same record reads current.

  5. List the odd inputs your probe can meet, such as nested repositories and symlinks. Add a case for each and assert that it yields a real value.

fingerprintgitguardrailsbuildsstaleness