Skip to content

fix(run-local): refuse stale ports, watch theme SCSS, log build generation - #8

Merged
ako merged 2 commits into
mainfrom
claude/mxbuild-diagnostics-spike-emta6h
Jul 22, 2026
Merged

fix(run-local): refuse stale ports, watch theme SCSS, log build generation#8
ako merged 2 commits into
mainfrom
claude/mxbuild-diagnostics-spike-emta6h

Conversation

@ako

@ako ako commented Jul 22, 2026

Copy link
Copy Markdown
Owner

Summary

Investigating a "my SCSS edit keeps serving old styles / stale-CSS-cache" report on mxcli run --local turned up three issues in the warm dev loop — none an actual compiled-CSS cache. Two are fixed here; the third (a suspected incremental-theme cache bug) was disproven by experiment.

Findings

# Problem Verdict
1 Without --watch there is no file watcher at all — mxbuild --serve only rebuilds on a /build request (startup / watch tick), so a save changes nothing in the running app. Confirmed, by design → docs.
2 A stale serve/runtime is silently adopted: nothing checked the ports were free, and the readiness probes (StartServe.waitReady / runtime waitAdminReady) treat "the port answers" as ready — which a leftover process satisfies. The fresh child (unable to bind) is torn down by defer, and the old process keeps serving old output. Reads exactly like a stale cache; it's a stale process. Confirmed, real bug → fixed.
3 Theme edits appeared not to recompile. Partially — the incremental theme step is fine (experiment below); the real gap was that the --watch signal was model-only (.mpr + mprcontents/), so themesource/**/*.scss was watched by nothing → fixed.

Problem-3 experiment (settles the "cache bug" question): one clean serve up → edit theme/web/main.scss (content change, no cache clear) → one /buildboth deployment/web/theme.compiled.css and theme-cache/web/theme.compiled.css changed and the new rule appeared. So there is no incremental-theme cache to clearrm -rf theme-cache/ .mendix-cache/ was a red herring.

Changes

eb0ec29 — code:

  • Stale-port guard (refuse, never kill): checkTargetPortsFree refuses to boot with an actionable message when :8080/:8090/:6543 already answer. Detection only — reaping another process is the user's call.
  • Watch theme source: themeSourceMTime (walks theme/ + themesource/ for .scss/.css/.js/.json) combined with the model signal in sourceMTime. Both are mtime-polling, so this is container-safe (unlike the rollup chokidar web-client watcher).
  • Build-generation logging: each apply logs build #N (boot is #1) so "did my change take?" is answerable from the log.
  • Tests: TestCheckTargetPortsFree, TestThemeSourceMTime_WatchesThemeAndThemesource.

b815db8 — docs: docs-site/src/tools/run-local.md, the synced skill .claude/skills/mendix/run-local.md, and a fix-issue.md symptom row — "SCSS needs a rebuild (--watch or a clean restart), never a cache-clear; kill the old serve/runtime first."

Testing

  • New unit tests pass; go build ./cmd/mxcli/..., go vet, and gofmt are clean.
  • The Problem-3 experiment was run against a fresh 11.12.1 project (via the real StartServe/Build path).
  • The only failing test in the package is the pre-existing TestServeIntegration (stale local /tmp/spikeapp fixture, v11.6.3 vs cached mxbuild 11.12.1) — unrelated to and untouched by these changes.

🤖 Generated with Claude Code

https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4


Generated by Claude Code

claude added 2 commits July 22, 2026 05:19
…eration

Investigating "SCSS edits don't show up in the running app" surfaced three
issues in the warm local loop — none an actual compiled-CSS cache:

1. Stale process silently adopted (the real time-sink). Nothing checked that
   the loop's ports were free before booting, and the readiness probes
   (StartServe.waitReady / runtime waitAdminReady) treat "the port answers" as
   "ready" — which a leftover mxbuild --serve/runtime satisfies. So a fresh
   `run --local` attached to the OLD process (its own child, unable to bind, was
   torn down by defer) and kept serving old output. Add checkTargetPortsFree:
   refuse to boot with an actionable message when :8080/:8090/:6543 already
   answer. Detect only, never kill — reaping someone's process is the user's
   call.

2. Theme source was watched by nothing. The --watch change signal was
   model-only (.mpr + mprcontents/), so editing theme/web/main.scss or
   themesource/<module>/web/*.scss triggered no rebuild even under --watch. Add
   themeSourceMTime and combine it with the model signal in sourceMTime; both
   are mtime-polling, so this is container-safe (unlike the rollup chokidar
   web-client watcher). A theme edit now rebuilds and hot-applies.

3. "Did my change take?" was unanswerable. watchAndApply already tracked a
   client-bundle generation but never surfaced it; log a monotonic build
   generation (boot is #1) on every apply.

The incremental theme step itself is fine — verified that one /build after an
SCSS content edit rewrites theme-cache/web/theme.compiled.css — so there is no
cache to clear; the "rm -rf theme-cache" ritual was a red herring.

Tests: TestCheckTargetPortsFree (free ports pass; an occupied port is named in
the refusal) and TestThemeSourceMTime_WatchesThemeAndThemesource (theme/ and
themesource/ edits advance the combined watch signal).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
…ss guidance

Document the outcome of the "SCSS cache" investigation across the two canonical
homes (docs-site page + synced skill) plus the dev symptom table:

- The --watch change signal now covers theme source (theme/, themesource/) as
  well as the model, and logs a build-generation counter.
- Editing SCSS needs a rebuild (--watch or a clean restart), never a cache
  clear — mxbuild --serve recompiles the theme on the next /build and picks up
  content changes, so `rm -rf theme-cache/ .mendix-cache/` is a red herring.
- "My edit didn't show up" is usually a stale serve/runtime still serving on
  the ports, not a cache: run --local now refuses to boot when the ports are
  taken, and the docs give the pgrep/kill/curl recovery and the "launch as the
  sole command, poll separately" guidance.
- fix-issue.md gets a symptom row mapping the whole class to runlocal.go.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
@ako
ako merged commit ce55796 into main Jul 22, 2026
5 checks passed
ako pushed a commit that referenced this pull request Jul 26, 2026
`retrieve … where [Seq = $Game/MoveSeq + 1]` failed with a bare
`mismatched input '+' expecting ']'`, with no hint that Mendix XPath
constraints take a literal/token/variable/path on the value side — never
an arithmetic expression. This is a Mendix limitation, not an mxcli bug,
so adding grammar support would only let through XPath that mxbuild
rejects.

Add an error hint keyed on `mismatched input '<+|*|div|mod>' expecting
']'` (the `expecting ']'` shape only occurs inside a bracketed
constraint) that names the limitation and the workaround: compute the
value into a variable first, then compare against it. Documented in
xpath-constraints.md; visitor test + negative bug-test example.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
ako pushed a commit that referenced this pull request Jul 26, 2026
`retrieve … where [Seq = $Game/MoveSeq + 1]` failed with a bare
`mismatched input '+' expecting ']'`, with no hint that Mendix XPath
constraints take a literal/token/variable/path on the value side — never
an arithmetic expression. This is a Mendix limitation, not an mxcli bug,
so adding grammar support would only let through XPath that mxbuild
rejects.

Add an error hint keyed on `mismatched input '<+|*|div|mod>' expecting
']'` (the `expecting ']'` shape only occurs inside a bracketed
constraint) that names the limitation and the workaround: compute the
value into a variable first, then compare against it. Documented in
xpath-constraints.md; visitor test + negative bug-test example.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
ako pushed a commit that referenced this pull request Jul 27, 2026
Three checker hints steered authors the wrong way:

- MDL001 (nested loop) recommended `retrieve $Match from $List where …
  limit 1`, which is a parse error — a plain retrieve cannot filter a
  list variable. Recommend `$Match = FIND($List, <cond>)` (in-memory,
  O(N)). Same broken advice fixed in CLAUDE.md idiom #2.
- MDL044 flagged `count()` used in an expression with "Did you mean
  'round()'?" — count is an aggregate activity, not a typo. For the known
  aggregates (count/sum/average/minimum/maximum) emit "assign it to a
  variable first: $n = count($List);" instead of a did-you-mean.
- MDL044's generic hint cited `mxcli syntax expressions`, which does not
  exist; point it at `mxcli syntax microflow`.

Also lock in finding #5's second trigger: a microflow-call output
variable reused across a fallback chain (try A, else try B) is CE0111.
It was already caught by the shared create-output-variable check
(`check --references`); add TestValidateDuplicateMicroflowCallOutputVar
(same-scope + nested-in-if) as a regression guard and document the
correct pattern (one variable per call + a plain set) in
write-microflows.md.

Reported from the RSS-reader build (findings #7, #8, #14c, #5).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
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.

2 participants