Warn when a local test run will recompile a running app's classes - #362
Merged
Conversation
§62's blanking is fixed, and the deployment tree has another half that is
shared. A local test run recompiles the project's Java into
`deployment/run/bin`, which is the classpath a live `mxcli run --local` is
holding open. Measured on a real 11.13 project: after one test run all 134
class files have NEW INODES and byte-identical content — every one deleted
and rewritten. A JVM loads classes lazily, so one it has not reached yet
can fail afterwards:
java.lang.NoClassDefFoundError: odatapushdown/QueryObject
What that costs is diagnosis, not the breakage. The microflows behind it
answer HTTP 200 with an EMPTY BODY — not a 500, not an error page — while
source-backed resources keep working, so half the app is fine and half
returns nothing, which is not a shape that suggests a test run did it. In
the reporting project it surfaced as 21 of 34 tests failing in a DIFFERENT
app, and 108 log lines went by before the two were connected.
mxcli cannot prevent it: mxbuild's Gradle pass owns the compile, and the
deployment directory cannot be moved — mxbuild writes it to
`<app dir>/deployment` and takes no option to change it. So it reports the
collision, which is the part that was missing, and says what the symptom
looks like and that the remedy is restarting that app.
It warns rather than refuses. The warm loop exists so an app can stay up
while you work on it, and the reporting project runs two apps that way as
a matter of course; refusing would break the workflow the feature is for.
Neither --attach nor --skip-build builds, so neither warns.
The fact needed — is a dev loop serving this project? — was already
published. `mxcli run --local` writes devLoopHandshake to
.mxcli/run-local.json for `mxcli constant set --apply`, carrying the pid
liveness check and the project identity this wants. A second state file
was written before that was noticed, and it would have CLOBBERED the
existing one, dropping the admin password and boot config that --apply and
--attach depend on. Reading what is already there is both correct and less
code.
Staleness is the whole feasibility question, not a detail: a `run --local`
killed, crashed, or ended by its development licence (§60, measured
lifetimes under six hours) leaves the file behind, and a warning driven by
the file alone would fire forever — a warning that is always wrong teaches
the reader to skip it. readDevLoopHandshake already refuses a dead pid, a
corrupt file and an absent one.
Verified end-to-end against a real `mxcli run --local`: the warning names
that app's actual pid and port, and the handshake still carries its
adminPass and 9 bootConfig keys afterwards. Controls: with the dev loop
stopped (handshake removed on exit) the same command is silent, and so is
a stale handshake with a dead pid.
Reported as mxcli-formula1 FINDINGS §81.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
mxcli-formula1 FINDINGS §81. §62's blanking is fixed; the deployment tree has another half that is shared, and this one cannot be fixed — only reported.
First, a correction to the finding, because it reads as contradicting two others. It was measured on
a739d2e2, which is the merge of #361 — so the runner works there (71/71 and 34/34, bundle surviving) because that PR fixed it. §81 credits2c0aa8d2 fix(test): give a local test run its own deployment tree; that is the commit which brokemxcli test --localfor every project (mxcli-ledger §150, mxcli-sudoku §51). §81 is a residual finding against the fixed build, not a disagreement with those two.What is still shared
A local test run recompiles the project's Java into
deployment/run/bin, which is the classpath a livemxcli run --localis holding open. Measured directly on a real 11.13 project — after one test run, all 134 class files have new inodes and byte-identical content. Every one deleted and rewritten. A JVM loads classes lazily, so one it has not reached yet can fail afterwards:What that costs is diagnosis, not the breakage. The microflows behind it then answer HTTP 200 with an empty body — not a 500, not an error page — while source-backed resources keep working. Half the app is fine and half returns nothing, which is not a shape that suggests a test run did it. In the reporting project it surfaced as 21 of 34 tests failing in a different app, and 108 log lines went by before the two were connected.
Why this reports rather than fixes
Same constraint as §150: mxbuild's Gradle pass owns the compile, and the deployment directory cannot be moved — mxbuild writes it to
<app dir>/deploymentand takes no option to change it. There is nowhere else to compile to.So the collision is named before the build, with the symptom and the remedy:
It warns rather than refuses. The warm loop exists so an app can stay up while you work on it, and the reporting project runs two apps that way as a matter of course — refusing would break the workflow the feature is for, to prevent something whose remedy is one restart. Neither
--attachnor--skip-buildbuilds, so neither warns.The mistake this nearly shipped, and how it was caught
Detecting the dev loop needs one fact: is a
mxcli run --localserving this project? I wrote a newRunStatefile at.mxcli/run-local.jsonfor it.Then a real
run --localwas started to verify the producer half — and the file that appeared was not mine.devLoopHandshakealready lives at that exact path, published by everyrun --localformxcli constant set --apply, carrying the same pid-liveness check and project identity, plusadminPassandbootConfig.The new reader parsed it fine, because JSON ignores unknown fields. The new writer would have silently dropped those two keys and broken
--applyand--attach. The duplicate is deleted and the existing handshake is read instead — correct, and less code.Every unit test was green against the duplicate. What caught it was running the producer for real, which is the same lesson #361 was about: assert the artefact, not the option.
Staleness is the feasibility question, not a detail
A
run --localthat was killed, crashed, or ended by its development licence (§60 — measured lifetimes under six hours) leaves the file behind. A warning driven by the file alone would fire forever, and a warning that is always wrong teaches the reader to skip it.readDevLoopHandshakealready refuses a dead pid, a corrupt file and an absent one; the warning inherits that rather than reading the file itself, and a test pins it.Verification
End-to-end against a genuinely running
mxcli run --local, not a fixture:adminPassand 9bootConfigkeys after the test run — the clobbering above would have removed both.Unit tests cover the message content (port, "recompil", "restart", and the
200symptom that nobody guesses), plus all three silent cases.go test ./...green,make vetclean,make check-mdl458 examples passing.🤖 Generated with Claude Code
https://claude.ai/code/session_018hifgRSawfaRWXS44YKtSJ
Generated by Claude Code