Skip to content

emrg: upgrade — make GUI build (npm run dist) unskippable and add app.asar freshness gate - #1043

Closed
argszero wants to merge 1 commit into
masterfrom
feature/gui-upgrade-build-not-skipped
Closed

emrg: upgrade — make GUI build (npm run dist) unskippable and add app.asar freshness gate#1043
argszero wants to merge 1 commit into
masterfrom
feature/gui-upgrade-build-not-skipped

Conversation

@argszero

Copy link
Copy Markdown
Owner

Problem

The upgrade flow's GUI build step is unreliable. The template line:

cd {{ upgrade_work }}/emrg/gui && npm install && npm run dist

was written in a single line, but the upgrade agent only ever executed the first command (npm install), either truncating the && npm run dist chain or rewriting it into a backgrounded npm install 2>&1 | tail -2 &. The result: npm run dist (electron-builder) never ran, so the GUI app was never rebuilt and stayed on the old upstream version with the broken layout — even though the version-printed emrg CLI reported the new version.

Evidence: across the v0.2.82 (12:59-13:01) and v0.2.83 (14:34) upgrades, the agent never once invoked npm run dist/electron-builder; it only ran ls -d dist/mac-arm64/EMRG.app against the pre-existing stale artifact and concluded the build was done.

Fix

Rewrite step 3 of emrg/server/prompts/upgrade_prompt.j2 so the two commands are separate, unskippable steps, and add a freshness gate that proves the artifact was actually rebuilt before copying:

  • 3bnpm install (must exit 0 before continuing)
  • 3cnpm run dist (marked MUST RUN — a stale dist will not rebuild on its own)
  • 3d — New freshness check: stat -f "%m %N" ... <EMRG.app>/Contents/Resources/app.asar must show an mtime AFTER this build started; if app.asar is missing or its mtime is old, the build did NOT run → do NOT copy, report and roll back.
  • 3e — re-seal the bundle + clear xattrs (renumbered from old 3c; unchanged)

This removes the trigger that let the agent silently reuse a stale app and ships a layout fix that was previously not installed.

Verification

  • Jinja2 template parses and renders with the upgrade_work variable.
  • .venv/bin/python -c "from emrg.client.app import run_client" — OK
  • .venv/bin/python -m emrg --help — OK
  • pytest tests/ -q: 1122 passed, 1 skipped (baseline)

@how2how2how2-arch

Copy link
Copy Markdown
Contributor

I tested this PR and it works as described:

Validation performed (head 9e6f364):

  • Rendered upgrade_prompt.j2 with the upgrade renderer — the template compiles cleanly; steps 3b (npm install), 3c (npm run dist), 3d (app.asar freshness gate) and 3e (re-seal) all present in order.
  • Confirmed stat -f "%m %N" syntax is valid on this macOS host (BSD stat) and returns an epoch-seconds mtime — the freshness gate command form is correct for the target platform.
  • tests/test_upgrade.py — 14 passed; full uv run pytest tests/ — 1122 passed + 1 skipped.

Observations (non-blocking):

  • The freshness gate is fail-closed: if dist/mac-arm64/EMRG.app is missing, stat errors with a non-zero exit, which correctly stops the copy step. Good.
  • The "within the last few minutes" criterion relies on the agent comparing against the build start time — reasonable for a prompt-level instruction. If this ever needs to be stricter, comparing app.asar mtime against npm run dist process start (e.g. recorded date +%s before step 3c) would make it deterministic.
  • Consider whether the same truncation risk applies to the other single-line && chains in this template (e.g. build-runtime) — worth a quick scan so the same bug class does not resurface elsewhere.

The root-cause analysis matches what I observed in the v0.2.82/v0.2.83 upgrade logs (npm install executed, electron-builder never invoked).

@argszero

Copy link
Copy Markdown
OwnerAuthor

Consolidating: this upgrade_prompt.j2 fix is byte-identical to the change in #1045, and #1045 is the canonical version — it additionally adds tests/test_upgrade.py::test_upgrade_prompt_gui_build_steps_are_separate_and_gated, which pins the fixed structure so the v0.2.82/v0.2.83 stale-GUI regression turns red immediately if the step split is ever reverted.

Closing this PR in favor of #1045 to keep a single source of truth and avoid a merge conflict (both touch the same line in upgrade_prompt.j2). The reviewer's note about other single-line && chains (lines 70/79/84) was verified: those are read-only version-compare (cd /tmp && npx asar extract-file), nvm setup (source ~/.nvm/nvm.sh && nvm use), and a node/npm version-verify (node --version && npm --version) — none silently skip a build like the fatal npm install && npm run dist chain, so no additional fix is needed there. The build-runtime step (3a) is a single command with no &&.

@argszero

Copy link
Copy Markdown
OwnerAuthor

Closing as duplicate of #1045 (canonical fix, adds the regression-guard test).

argszero added a commit that referenced this pull request Aug 27, 2026
…2/v0.2.83 stale-GUI regression) (#1045)
* emrg: upgrade — split GUI build into unskippable npm install + npm run dist steps with app.asar freshness gate
* emrg: test — guard upgrade_prompt.j2 GUI build step structure (v0.2.82/v0.2.83 stale-GUI regression)
v0.2.82/v0.2.83 upgrades ran only 'npm install', never 'npm run dist'
(electron-builder) — the single-line '&&' chain in upgrade_prompt.j2 was
truncated by the upgrade agent, so the GUI stayed on the old build.
#1043 fixes the prompt (split steps + app.asar freshness gate); this test
pins that structure so the same regression turns red immediately:
- npm install and npm run dist must stay on separate lines (no single-line
chain), dist step must carry a MUST RUN directive
- app.asar mtime freshness gate (stat) must exist and come after dist
Negative-state verified against the pre-#1043 prompt (2deb518): all three
assertions fire on the old structure. Python tests 1123 -> 1124, Agent.md
synced.
* emrg: test — guard upgrade_prompt.j2 macOS re-seal chain (rant 2026-08-25T09:18:19)
Completes the upgrade_prompt structure guard: #1045 (R2249) pinned the GUI
build steps (npm install / npm run dist / app.asar freshness gate); this
pins the macOS deployment-safety chain that ships the artifact:
- re-seal (codesign --force --deep --sign -) must occur BEFORE the
Replace/copy step — copying an unsealed bundle ships the malware-flagged
artifact (macOS 26 moves unsealed+xattr copies to Trash)
- built-artifact xattr clear must precede the copy (ditto propagates xattrs)
- run-copy xattr clear (~/Applications/EMRG.app) must exist
- at least 3 codesign --verify --deep --strict checks (pre-copy sanity +
install + run copy)
Python tests 1124 -> 1125, Agent.md synced.
---------
Co-authored-by: EMRG Evolution <emrg@argszero.dev>
Co-authored-by: argszero <argszero@gmail.com>
Sign up for freeto 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.

2 participants

@argszero@how2how2how2-arch