Skip to content

exec.sh performance on Windows (Git Bash): where the time went #584

Closed

Description

@mkr87

Field report, not a bug list. One small Stage 5 project (3 MDL scripts so far), Windows 11
Enterprise, Git Bash, mxcli v0.28.0, Mendix 11.12.6, toolkit c28fc0d. No project content quoted.

Headline: a script whose mxcli check takes 1.3 s and whose write takes 1-2 s costs 64-227 s
per exec.sh run. The model work is under 5% of that. Most of the rest is process-spawn overhead
in the bash helpers, multiplied by a slow spawn on this machine, plus two cold mxbuild runs.

Measurements

The ledger is .claude/loop/metrics-ledger.tsv (9 runs). Per-step times come from BUILD-LOG.

Run outcome Count Elapsed
applied 4 64, 65, 83, 212 s
gate failed (real CE error) 1 227 s
blocked (uncommitted model) 4 8, 10, 15, 19 s
exec.sh step Typical Notes
guards ~9 s
check 11-17 s mxcli check 1.3 s; script-owners.sh 2.8 s; mxtk_ensure_mxbuild 7.2 s
snapshot ~5 s
baseline 6-157 s 157 s = first mxbuild of the session (cold JVM/caches)
exec 1-2 s the actual write
mxbuild gate 18-20 s ~156 s when cold
lint ~7 s
score ~5 s

Machine baseline: a trivial process spawn (/usr/bin/true) costs ~94 ms. mxcli --version
costs ~420 ms. Defender real-time protection is on. Network, proxy and tunnel were all ruled out:
local disk, no VPN or tunnel active, and no network calls on the exec.sh path. Corporate Windows
laptops with endpoint protection commonly look like this, so it is probably typical for toolkit
users and not an outlier.

Findings, ranked by time saved

  1. mxtk_ensure_mxbuild costs 7.2 s per run. It resolves the model version and the mxbuild
    path through many subprocesses, and repeats the work on every exec. Setting MXBUILD_PATH
    (in .claude/settings.local.json env) cut it to 1.6 s. The remainder is
    mxtk_model_version, still called for the mismatch warning.
    Suggest: memoize the model version and the mxbuild path per exec (or cache them keyed on
    the .mpr mtime), and have preflight recommend MXBUILD_PATH on Windows.
  2. Cold mxbuild costs ~150 s, once per session, in baseline or gate. It is unavoidable, but
    it is invisible: the run looks hung.
    Suggest: print "first mxbuild of this session, expect ~2-3 min", or warm mxbuild in the
    background at session start.
  3. Uncommitted-model refusal: 4 of 9 runs were blocked (8-19 s each, plus the human round
    trip). The second-exec idempotence check requires a commit in between, and agents keep running
    into this.
    Suggest: make the refusal message say exactly which commit to make, or allow the immediate
    idempotence re-run against the snapshot exec.sh just took.
  4. Helper-script spawn count. guards (9 s), snapshot (5 s), score (5 s) and
    script-owners.sh (2.8 s) are almost all spawn overhead at ~94 ms each.
    Suggest: collapse the grep | sed | awk chains into single awk/bash builtins on the hot
    path; add MXTK_PROFILE=1 to print the spawn count and time per step so users can see this
    themselves.
  5. The lint gate exits 2 on every run with no baseline (ERROR-NO-BASELINE, 0 errors). This
    costs no time, but it reads like a failure and sent investigation the wrong way.
    Suggest: state "no baseline yet: run bin/lint-gate.sh --update-baseline" in the exec.sh
    summary.
  6. Preflight could detect slow spawn. Time 20 spawns. If the average is over ~30 ms, warn
    and point to the two real fixes: Defender exclusions for the project dir, Git's usr/bin and
    the JDK, or running inside WSL2 / the devcontainer.

Smaller friction hit in the same session

  • Playwright is not installed by the toolkit, so the journey runner fails on first use.
  • The journey runner guesses the app port and admin password wrongly. It should read them from
    the run config.
  • check-design-reaches-app.sh has an argument-handling bug.
  • The dotted row-ID regex rejects valid IDs.

Activity

  1. MendixMau commented on Oct 28, 2026

    @MendixMau
    Owner

    Triage: DISCUSS track. Performance on the exec.sh path in every build entry mode. The two items it shares with #585 (mxtk_ensure_mxbuild memoization, spawn-heavy helpers) are tracked there. Partly verified.

    Problem, by finding:

    1. mxtk_ensure_mxbuild → mxtk_model_version is called unconditionally (project-bin/_common.sh:1050). Verified; this is exec.sh: ~69 s of bookkeeping plus a 165 s deploy-build gate for a 1 s model write (Windows profile) #585 option 2.
    2. A cold mxbuild prints no notice. Verified: the gate gives no "first build" warning.
    3. The uncommitted-model refusal already names the exact commit, and --commit-first makes that commit for you (project-bin/exec.sh:828-829). The part still open is the idempotence re-run needing a commit in between.
    4. Spawn count: see exec.sh: ~69 s of bookkeeping plus a 165 s deploy-build gate for a 1 s model write (Windows profile) #585.
    5. Lint with no baseline reads as a failure. Verified. lint-gate.sh:597 exits 2 (ERROR-NO-BASELINE). exec.sh then falls through to *) "lint could not run (lint-gate exit 2)" (exec.sh:1140). The --update-baseline hint only reaches the captured log, not the summary. This one is a FIX-track sub-item: a summary message, no verdict change.
    6. Slow-spawn detection in preflight: not present. It would add about 20 spawns to preflight.

    Value: Medium, beyond what #585 covers. Item 5 is Low effort with a clear payoff: it sent this investigation the wrong way.

    Options:

    1. Messages only.

      • Add a case in exec.sh's lint summary: 2 + ERROR-NO-BASELINE → "no baseline yet: run bin/lint-gate.sh --update-baseline".
      • Print a cold-mxbuild notice when no warm build has run this session.

      No process or performance change. Small.

    2. Option 1, plus a preflight spawn probe that warns above about 30 ms and points to Defender exclusions and WSL2/devcontainer. Adds about 2 s to preflight on Windows.

    3. Option 1, plus an idempotence re-run against exec.sh's own fresh snapshot, with no commit needed. This changes the refusal guard, so it needs guard rules 6 and 7 checked.

    Recommendation: 1. It is cheap and stops the misreads. 2 is worth adding after #585's profiler shows how common slow spawn is.

    Smaller friction items: Playwright not installed, the journey runner's port and password guess, the check-design-reaches-app.sh argument bug, and the dotted row-ID regex. These are UNVERIFIED and need repro detail. @mkr87, could you file each as its own issue with the command and the output? Then each can go down the FIX track.

    Decision needed from you: build option 1, or 2 or 3? Reply "go option N" to build it.


    Generated by Claude Code

  2. MendixMau commented on Nov 3, 2026

    @MendixMau
    Owner

    Thanks @mkr87 — closing as fixed: the exec.sh Windows / Git Bash time sinks you measured were addressed by #596 and #597, both on master. Please reopen with a fresh timing if exec.sh is still slow for you on current master.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions