Skip to content

Rebuild #25 on current master + doctor "Gate self-test" (supersedes #25) - #76

Draft
MendixMau wants to merge 9 commits into
masterfrom
claude/pr25-rebuild
Draft

MendixMau wants to merge 9 commits into
masterfrom
claude/pr25-rebuild

Conversation

@MendixMau

Copy link
Copy Markdown
Owner

What

Two parts, one branch, rebuilt on current master so it no longer conflicts with Marco Keijsers' Windows fixes (#59).

Part 1: #25's Windows onboarding work, minus what master already carries.

Part 2: doctor "Gate self-test".
Doctor probed whether mxbuild and Java were present. It never asked whether the gate could see an error. The wrong-arch mxbuild incident and #59 were both "gate reports green on a broken build". The new section (full run, or --gate-selftest alone, skipped under --quick) copies the model into a scratch dir, reads a baseline count that must be a real integer, injects a CE0117-shape type mismatch via the project's own mxcli, and requires the count to move from 0 to at least 1. Bounded by DOCTOR_GATE_TIMEOUT (300s), scratch removed on every exit path. If mxcli refuses the injection the verdict is NOT RUN with mxcli's own message, never a false "blind". Receipt line gate-selftest: … lands in .claude/.doctor-receipt.

Supporting refactor: exec.sh's three inline mxbuild run-and-read blocks became one _common.sh function, mxtk_mxbuild_error_count. Downstream branching untouched. The real gate stays unbounded by default (MXTK_GATE_TIMEOUT opt-in), because a timed-out gate would read as FAIL and auto-restore good work.

Why draft

Neither part has a real field run yet:

  • Part 1: no Windows / Git Bash run of the rebuilt precedence and .exe-over-ELF behaviour.
  • Part 2: no run against a real mxbuild. The injected MDL must be accepted by a real mxcli exec and must produce at least one mxbuild error. The stub fixtures prove the logic, not the assumption.

Ready to flip when one of those runs is cited on this PR (Marco or Clinton are the obvious candidates). Per CLAUDE.md "field-proof before merge".

Checks

check-scripts.sh 76/76 + 20/20, check-portability.sh clean, leak guard clean, render-routing.sh --check in sync, check-pr-discipline.sh clean, tests/run-tests.sh 19/19, test-bug07-08.sh 28/28 (exec.sh gate discriminators unchanged), test-doctor-docker-probe.sh 13/13, test-common-windows.sh 10/10 (new), test-doctor-gate-selftest.sh 20/20 (new).

Supersedes #25.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw


Generated by Claude Code

claude and others added 9 commits September 16, 2026 05:49
…l locations

- find_sp_app: newline-join listings, dedupe identical Program Files roots,
  accept only version-shaped dirs (gradle-8.5 was winning sort -V glued to
  the next entry)
- check-scripts: project without bin/ is a WARN pointing at init-project
- find_project_mxcli: mxcli.exe first on Git Bash; an ELF ./mxcli (the Dev
  Container's) is named, not chmod-ed; --install fetches the .exe beside it;
  exec.sh, lint-gate, review-module, conformance-check, fixture-manifest,
  page-scope resolve the same way
- save-sp/restart-sp section is a note off macOS, not a WARN
- toolkit.env (<project>/.claude/toolkit.env, ~/.mxcli-toolkit.env) loaded
  by _common.sh: MENDIX_APP, JAVA_HOME, MXBUILD_PATH, MXCLI_VERSION, PYTHON;
  Windows paths pasted as-is; doctor reports, self-gitignores, flags a JRE
  without javac; gate/verdict wording guides instead of alarms

Rebased onto current master, which already carries Marco Keijsers'
find_sp_app rewrite (532e359/737cbe3/368cd2e: a modeler/mxbuild.exe
existence loop, native_path(), file-capture of mxbuild output). Conflict
resolution in find_sp_app: dropped this commit's own glued-listing fix
(newline-join workaround) and its trailing version-shaped-directory regex
filter — both are superseded by master's existence loop, which appends
one directory at a time (already newline-terminated, so nothing glues)
and admits only a directory carrying modeler/mxbuild.exe (so gradle-8.5
can never be a candidate). Kept this commit's seen-based root
de-duplication wrapped around master's loop: not redundant with it, since
master's loop filters entries within one root's listing but never dedupes
scanning the same physical folder twice when ProgramW6432 and
PROGRAMFILES point at the same directory.

The CHANGELOG's 'Fixture-proven here' claim is addressed in a follow-up
commit (a real fixture is added, and the line is reworded to what it
actually proves).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
… verdict wording corrected

The Windows field run this instrument change needs is still owed; the PR
stays draft until it is cited.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
project-bin/exec.sh hand-wrote the same sequence three times: run mxbuild
with --write-errors, capture output to a file (never `$(...)` — Studio Pro
11's mxbuild.exe on Windows starts a helper deno.exe that inherits stdout
and never closes it, so a command substitution waits forever for EOF that
never comes), close stdin, and treat an empty errors file + exit 0 as a
verified 0 (SP11 only writes --write-errors when the project already has
errors). One copy for the pre-flight baseline, one for the main gate, one
for the post-restore "whose error is it" check.

Pulled the run+capture into project-bin/_common.sh's new
mxtk_mxbuild_error_count(), so bin/doctor.sh's upcoming gate self-test can
call the exact code a real exec run trusts, not a fourth copy that could
quietly drift from it. It also adds a genuine new behaviour the old code
never had: a portable timeout (background process + poll + kill, the same
pattern as doctor.sh's docker_daemon_up — no timeout(1) is assumed, macOS
ships none), overridable via MXTK_GATE_TIMEOUT, default 300s, generous
enough that no real build is ever cut short by it.

exec.sh's three call sites now call the shared function and keep their own
downstream logic (err_count/err_codes/err_set, the delta-gate, the restore,
the detailed error printing) unchanged — the function only replaces the
"run it and get a file + exit code" mechanics, not the reading of codes or
messages. Each call site adds `|| true` after the call: exec.sh runs under
`set -e`, and the function's own return code (0 = count printed, 1 =
unparseable, 2 = mxbuild/java not found, 3 = timed out) is not meant to
abort the script — the existing file-presence/exit-code branching below
each call site already decides the gate's verdict from $MXTK_MXBUILD_EXIT
and the file's content, exactly as before.

Behaviour is unchanged: tests/wave2/test-bug07-08.sh project-bin/exec.sh
still passes all 28 assertions, including case G (mxbuild exits non-zero
with no errors file -> gate fails, not "unverified") and case H (mxbuild
absent -> gate skipped, exit 0) — the two cases most at risk of blurring
together under the new function's own three-way return code.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VJgWP5vEoAsNsJYqCDMGNw
Every doctor.sh section up to this one answers "is mxbuild present and
does it run" — none answers "if the model had a real error, would the
gate see it". That gap is exactly how a wrong-architecture mxbuild once
reported 0 errors for several commits before anyone noticed (merge
desk, after Marco Keijsers' #59 and the July wrong-arch mxbuild
incident).

New "Gate self-test" section (full run only; skipped under --quick,
forceable alone via `doctor.sh --gate-selftest [project-dir]`) runs the
exact function exec.sh's own gate trusts — mxtk_mxbuild_error_count,
project-bin/_common.sh, the mandatory chain step that produces every
count this section reads — against a scratch copy of the real project
model:

1. Baseline: the function must resolve a real integer off the
   untouched copy. "?" here means the gate cannot read mxbuild's own
   output — the original F-042-class defect.
2. Known-bad control: inject a deliberate CE0117-shape type mismatch
   via the project's own mxcli (throwaway module/microflow, quoted
   identifiers), then require the count to go from 0 to >=1. A gate
   that reports 0 on a model it just broke is worse than no gate: it
   is green on a broken build.

The scratch directory is removed on every exit path via a RETURN trap,
never touching the project's real .mpr/mprcontents. Bounded by
DOCTOR_GATE_TIMEOUT (default 300s, same bound exec.sh's gate uses).
NOT RUN (not FAIL) when there's no project/.mpr, no mxbuild/java, or
no project mxcli resolved — this is a capability gap, not a defect.

No real-mxbuild field run yet; fixture-proven with stubs only (next
commit). Windows run still owed.
Stubs a fake project mxcli (marks a scratch model "bad" when it sees
the self-test's injected MDL) and a fake mxbuild with four behaviours
(MODE_GATE_STUB): healthy (0 then >=1 — the only PASS), no-file (never
writes an errors file, exits non-zero — the original "?" defect),
always-clean (always reports 0, even on the known-bad model — the
gate-is-blind case), and slow (sleeps past DOCTOR_GATE_TIMEOUT).

Covers: healthy -> PASS with scratch dir cleaned up and receipt
recording it; no-file -> FAIL naming the unreadable error file;
always-clean -> FAIL naming "gate is blind"; slow with
DOCTOR_GATE_TIMEOUT=2 -> FAIL naming the timeout, doctor still returns
promptly; no mxbuild resolvable (MXCLI_HOME redirected off this
machine's real ~/.mxcli/mxbuild cache, since find_mxcli_cache doesn't
go through MXBUILD_PATH/MENDIX_APP) -> NOT RUN, not FAIL; --quick alone
omits the section; a full run includes it without the flag.

bash tests/wave2/test-doctor-gate-selftest.sh bin/doctor.sh -> 16
passed, 0 failed.
Unit-tests mxtk_load_env, mxtk_is_elf and find_project_mxcli directly,
each sourced fresh in its own subshell (PROJECT_ROOT/MXTK_ENV_LOADED
are resolved once at source time, so re-sourcing in one long-lived
shell would carry a previous case's environment forward). HOME is
redirected per case so this machine's own ~/.mxcli-toolkit.env — absent
here, but not guaranteed absent everywhere — can never leak in.

Covers: a pre-set env var beats both files; the project file
(.claude/toolkit.env) beats the user file (~/.mxcli-toolkit.env); the
user file is used as the last resort and named in MXTK_ENV_LOADED; a
Windows-form path (C:\...) is converted to Git Bash's /c/... form;
mxtk_is_elf tells a real ELF binary from a shell script; and
find_project_mxcli, on a stubbed Git Bash platform (OSTYPE=msys),
prefers mxcli.exe over a same-directory ELF mxcli, refuses an
ELF-only mxcli outright, while the identical ELF mxcli is accepted
fine on a real Linux/container platform.

bash tests/wave2/test-common-windows.sh project-bin/_common.sh -> 10
passed, 0 failed.
… line

Two changes, both under 2026-09-16:

1. #25's original line claimed "Fixture-proven here (glue, precedence,
   .exe-over-ELF)" with no fixture behind it anywhere in that PR, and
   still described the find_sp_app "glue" fix this rebuild drops in
   favor of Marco Keijsers' #59 (already on master, same defect fixed
   a different way). Reworded to name what actually changed in this
   rebuild, to note the dropped glue fix and why, and to cite the new
   tests/wave2/test-common-windows.sh (10/10) for what it actually
   proves — precedence, path conversion, is_elf, and .exe-over-ELF on
   a stubbed Git Bash platform. Windows field run still owed; draft
   until cited here.

2. New line for the gate self-test (bin/doctor.sh, project-bin/exec.sh,
   project-bin/_common.sh): what it does, why (Marco Keijsers' #59 and
   the July wrong-arch-mxbuild incident), and that it is fixture-proven
   with stubs only — no real-mxbuild field run yet, Windows run owed,
   draft until cited here.
…ection is NOT RUN, not blind

exec.sh's three mxbuild sites went through the new shared function with a
300s default bound. master had none: a large model's deploy build can run
longer, and a timed-out gate reads as exit!=0 + no errors file = FAIL =
auto-restore of work that was fine. exec.sh now passes 0 (unbounded) unless
MXTK_GATE_TIMEOUT is set; doctor's scratch-copy self-test keeps 300s.

If the project's mxcli refuses the known-bad injection, the copy is still
clean and the old code reported the gate as blind. Now NOT RUN with mxcli's
own message. Fixture case 8, 20/20.

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