Skip to content

Catch the staleness the bundle guard could not see - #391

Merged
dovvnloading merged 1 commit into
mainfrom
fix/spa-staleness-guard-checkout
Sep 1, 2026
Merged

dovvnloading merged 1 commit into
mainfrom
fix/spa-staleness-guard-checkout

Conversation

@dovvnloading

Copy link
Copy Markdown
Owner

Problem

The Stop-hook guard (tools/rebuild-spa-if-stale.sh) rebuilds web_ui/dist/app whenever the bundle is older than web_ui/src. That solves one staleness problem and is silent, by construction, about a second one.

If the checkout is behind the branch work lands on, merged code is not in the working tree at all. The bundle is then perfectly consistent with the source, the mtime walk finds nothing newer, and the guard correctly says nothing - while the running app is missing features that already shipped.

That is not hypothetical: a canvas went on spawning placeholder nodes on double-click for hours after the commit removing that gesture had merged, because the checkout sat five commits behind origin/main and every local file legitimately matched the bundle built from it.

Change

  • The bundle records the commit it was built from (dist/app/.built-from-commit") and is rebuilt when HEAD` no longer matches it. A pull or branch switch that leaves mtimes alone is invisible to an mtime walk; a recorded HEAD is not.
  • When HEAD is behind origin/main the hook reports it - naming the branch and the commit count - whether or not it also rebuilt. Reported, never auto-merged: the working tree belongs to the developer and a Stop hook has no business rewriting it. It reads only already-fetched refs, so it never blocks on the network.

Test plan

Exercised directly against a real checkout sitting five commits behind origin/main:

  • With no commit stamp present: rebuilds, then reports "rebuilt. BUT this checkout (ux/text-contrast-wcag-tiers) is 5 commit(s) behind origin/main...".
  • Immediately re-run with the bundle current: no rebuild, and still reports the checkout is 5 commits behind.
  • Touching a source file with the checkout current: rebuilds as before, so the original mtime behaviour is unchanged.
  • On a checkout level with origin/main: silent, exit 0.

The Stop-hook guard rebuilt web_ui/dist/app whenever the bundle was older
than web_ui/src. That solves one staleness problem and is silent, by
construction, about a second one.

If the CHECKOUT is behind the branch work lands on, merged code is not in
the working tree at all - so the bundle is perfectly consistent with the
source, the mtime walk finds nothing newer, and the guard correctly says
nothing while the running app is missing features that already shipped. A
canvas went on spawning placeholder nodes on double-click for hours after
the commit removing that gesture had merged, because the checkout sat five
commits behind origin/main and every local file legitimately matched the
bundle built from it.

Two additions:

- The bundle records the commit it was built from
  (dist/app/.built-from-commit) and is rebuilt when HEAD no longer matches
  it. A pull or branch switch that happens to leave mtimes alone is
  invisible to an mtime walk; a recorded HEAD is not.
- When HEAD is behind origin/main the hook says so, naming the branch and
  the commit count, whether or not it also rebuilt. Reported, never
  auto-merged - the working tree belongs to the developer, and a Stop hook
  has no business rewriting it. It reads only already-fetched refs, so it
  never blocks on the network.

Co-Authored-By: Claude <noreply@anthropic.com>
@dovvnloading
dovvnloading merged commit 3393717 into main Sep 1, 2026
4 checks passed
@dovvnloading
dovvnloading deleted the fix/spa-staleness-guard-checkout branch September 1, 2026 17:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant