"Served-bundle version drift — live site announced v0.1.0 after the 1.0.0 release; refreshed bundle + version-sync gate coverage for apps/console/web"
§1
apps/console/web/(the tracked vite output the VPS serves via git pull) MUST be rebuilt soversion.jsoncarries the current VERSION - done:{"build":"20260712204020","version":"1.0.0"}; the new service-worker cache id makes connected clients pick up the deploy via the existing update banner (useUpdateCheck polls version.json).check-version-sync.shMUST cover the served bundle as artifact 7:apps/console/web/version.json .version == VERSION, failing with the exact rebuild command. This makes bundle lag loud at every pre-commit, payload-gate, version bump, and release run that already calls the gate (TASK-IMP-068 wiring).- The check reads the REPO copy, not the payload - the served bundle is a repo-side channel like the status page, and it rides the same gate chain rather than a new one.
Lean profile: one gate check + one regenerated bundle; defect, fix, and both gate directions machine-verified in-session.
§2 — Why this shape
The badge pipeline itself was already correct (VersionBadge -> /version.json -> stamp-sw.mjs -> root VERSION); what was missing is anything that forces the TRACKED output to be regenerated after a version change. The gate converts "someone must remember to rebuild" into "the next commit fails with the command to run" - same posture as every other artifact in the sync check. A CI leg that rebuilds and ships the bundle (docs-job style, via ship.sh) would remove even that manual step; recorded in §9 as the structural follow-up rather than done here, because it changes the serving model (git-pull-tracked dir) that deploy.sh's caddy bind documents.
§5 (run 2026-07-13)
- Rebuild:
npm run buildgreen (tsc + vite 142ms + stamp-sw); version.json 0.1.0 -> 1.0.0, sw cachecyberos-shell-20260712204020; js asset hashes unchanged. PASS - Positive gate:
sync OK 1.0.0 across 7 artifacts. PASS - Negative gate: version.json temporarily set to 0.9.9 ->
DRIFT ... 0.9.9 != 1.0.0 (stale served bundle - rebuild: cd apps/web && npm run build), exit 10; file restored. PASS - Sandbox note: the build needed
@rolldown/binding-linux-arm64-gnuadded to node_modules (mac-installed tree, linux sandbox) ---no-save, lock/manifest untouched. - Testing pass 2026-07-13 (post gate-1 "approve all"): positive gate re-run green (
sync OK 1.0.0 across 7 artifacts); negative gate re-run green (version.json set to 0.9.9 ->DRIFT ... 0.9.9 != 1.0.0 (stale served bundle - rebuild: cd apps/web && npm run build), exit 10, file restored,git status --shorton the file clean after restore).
§9
- Structural follow-up: a CI leg that rebuilds apps/web on
apps/web/**/VERSION changes and ships it like the docs job (ship.sh pattern), retiring the tracked-output model. Decide together with the deploy.sh caddy-bind implications; not blocking 1.0.0. - The old sw cache id lingers in clients until their next poll; the update banner handles it (existing machinery, no change).
§10
| Failure | Detection | Recovery |
|---|---|---|
| version bump without web rebuild | gate check 7 fails the next commit/CI run with the command | run the printed rebuild, commit |
| version.json deleted/mangled | check 7 explicit missing/unreadable branch | rebuild |
| rebuild forgotten AND gate bypassed | impossible via the normal chain (pre-commit + payload-gate + version.yml + release all call the gate) | any one path fires |
| badge shows stale after deploy | sw cache id changed -> update banner prompts reload | client reload |
End of TASK-IMP-080.
Audit
- ISS-001: draft only rebuilt the bundle (restores truth today, silently drifts again on the next bump) - resolved: gate coverage added as artifact 7 in check-version-sync.sh, riding the existing TASK-IMP-068 chain instead of a new mechanism; both directions tested.
- ISS-002: draft proposed a CI rebuild-and-commit leg in the same change - resolved: deferred to §9; it alters the documented git-pull serving model (deploy.sh caddy directory bind) and deserves its own decision, while the gate already removes the silent-failure mode. Score = 10/10.