The portal serves Frappe assets from a host copy (/opt/erpnext-assets). Frappe
emits content-hashed filenames that change on every ERPNext rebuild/migrate; the
host copy was never re-synced by deploy.sh, so the manifest referenced hashes
that 404'd on the host -> portal rendered with no CSS (recurring issue).
- Commit extract-erpnext-assets.sh (was untracked, prod-only). It now also runs
bench build to keep assets.json consistent with dist/, copies the manifest,
and verifies the login bundle exists on the host before finishing.
- deploy.sh: add an 'erpnext' target that rebuilds, runs bench migrate, and
re-extracts assets. Plus a cheap drift guard on EVERY deploy that auto-heals
by re-extracting if the portal manifest references a missing CSS bundle.