Skip to content

fix(mise): orchestrate elm builds serially to eliminate Windows race#1281

Merged
DamianReeves merged 2 commits into
mainfrom
worktree-fix-windows-elm-race
May 28, 2026
Merged

fix(mise): orchestrate elm builds serially to eliminate Windows race#1281
DamianReeves merged 2 commits into
mainfrom
worktree-fix-windows-elm-race

Conversation

@DamianReeves

@DamianReeves DamianReeves commented May 28, 2026

Copy link
Copy Markdown
Member

Summary

Follow-up to #1280. The Windows build is intermittently failing with -- PROBLEM BUILDING DEPENDENCIES -- on elm/html 1.0.0 (e.g. PR #1280 run 26558683076, run 26559185295) because elm make is not concurrent-write-safe: multiple processes race on ~/.elm/<package>/artifacts.dat and on the per-project elm-stuff/. Linux mostly wins the race thanks to friendlier file-locking semantics, but the bug is the same on both platforms — the recent passing Windows runs were the race tipping the other way, not a real fix.

The previous round of fixes added //MISE depends= headers expecting mise to serialize. mise reads those headers but its scheduler parallelizes any tasks whose deps are satisfied — both Linux and Windows logs show build:cli, build:cli2, build:dev-server, build:try-morphir, and build:treeview starting within ~1 ms of each other once build:check-elm-docs finishes.

Changes

  • .mise/tasks/build.ts — drive the elm-touching tasks via an explicit mise run --skip-deps <task> chain: check-elm-docs → cli → cli2 → dev-server → try-morphir → components → morphir-ts. --skip-deps prevents each subtask from re-running its declared deps. build:treeview (webpack only) runs in parallel alongside the chain.
  • .mise/tasks/build/dev-server.ts — serialize the three internal elm make calls; they all target cli/elm.json, so the same race that motivated the build:cli serialization applies here.

build:morphir-ts is in the elm chain because it invokes elm indirectly via node-elm-compiler in cli/morphir-elm.js (the "Compiled in DEV mode" output in its logs is from elm).

Test plan

  • Build passes locally on Windows from a cold cli/elm-stuff / cli2/elm-stuff / elm-stuff
  • CI passes on ubuntu-latest
  • CI passes on windows-latest

When the elm package cache (~/.elm) or per-project elm-stuff/ is cold,
multiple concurrent `elm make` invocations race on
~/.elm/<package>/artifacts.dat and the shared elm-stuff/ files,
producing the "PROBLEM BUILDING DEPENDENCIES" failure that's been
intermittently breaking the Windows CI build (e.g. PR #1280's
26558683076 and 26559185295 runs). Linux mostly avoids it thanks to
friendlier file-locking semantics but the underlying race is the same.

The previous round of fixes added `//MISE depends=` headers expecting
mise to serialize tasks. mise reads those headers but its scheduler
parallelizes any tasks whose deps are satisfied — both Linux and
Windows CI logs show cli, cli2, dev-server, try-morphir, and treeview
starting within ~1ms of each other after check-elm-docs finishes. The
recent passing runs were the race coming out the other way, not a
real fix.

Changes:
- .mise/tasks/build.ts: drive the elm-touching tasks via an explicit
  `mise run --skip-deps <task>` chain (check-elm-docs → cli → cli2 →
  dev-server → try-morphir → components → morphir-ts). --skip-deps stops
  each subtask from re-running its own declared deps. treeview (webpack
  only) runs in parallel alongside the chain.
- .mise/tasks/build/dev-server.ts: serialize its three internal elm
  makes — they all target cli/elm.json, so the same race that motivated
  the build:cli serialization applies to them.

morphir-ts is in the elm chain because it invokes elm indirectly via
node-elm-compiler in cli/morphir-elm.js (visible as "Compiled in DEV
mode" in its output). Verified locally on Windows with a cold elm-stuff:
full build completes successfully.
Add `//MISE sources=[...]` / `outputs=[...]` to every build subtask so
mise can skip a task whose inputs haven't changed. The orchestration in
build.ts already invokes each subtask via `mise run --skip-deps`, so the
freshness check kicks in on every step independently.

Cold rebuild from a clean tree still does the full work serially. Warm
rebuild with no source changes finishes in seconds (mise reports
"sources up-to-date, skipping" for each elm task). Touching a single
.elm file rebuilds only the affected tasks — for example, touching
cli/src/Morphir/Web/TryMorphir.elm reruns cli, dev-server, try-morphir,
components, and morphir-ts but leaves check-elm-docs, cli2, and treeview
skipped.

Notes:
- Patterns are deliberately broad (`src/**/*.elm`, `cli/src/**/*.elm`)
  rather than module-precise. elm tracks transitive imports itself, so
  conservative globs trade a few extra rebuilds for safety.
- build:components has no sources/outputs because its output overlaps
  its input (concat insight.js + morphir-insight-element.js → insight.js),
  which would make mtime-based freshness ambiguous. It's a fast concat
  and always re-runs.
- Removed the Promise.all that ran build:treeview alongside the elm
  chain — the speedup was marginal and the fully serial form is easier
  to reason about now that each subtask gets independent freshness
  caching.
@DamianReeves DamianReeves merged commit 1956c36 into main May 28, 2026
4 checks passed
@DamianReeves DamianReeves deleted the worktree-fix-windows-elm-race branch May 28, 2026 08:32
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