Skip to content

Warm local dev loop (mxcli run --local) + provisioning#768

Merged
ako merged 28 commits into
mendixlabs:mainfrom
ako:main
Jul 19, 2026
Merged

Warm local dev loop (mxcli run --local) + provisioning#768
ako merged 28 commits into
mendixlabs:mainfrom
ako:main

Conversation

@ako

@ako ako commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator

Brings the fork's unshipped work upstream: the warm local dev loop and the
proposals that led to it. 28 commits (two prior fork PRs), no workflow changes.

Implementation — mxcli run --local (slices 1–2 of the warm-loop proposal)

  • Docker-free warm loop: long-lived mxbuild --serve + standalone Mendix runtime;
    a model edit hot-applies via reload_model (page/microflow/text) or restart+DDL
    (entity/view/association), chosen from the serve build's restartRequired.
  • Browser client bundling (rollup) so pages render; --watch keeps an incremental
    bundler hot (CHOKIDAR_USEPOLLING for container fs).
  • Playwright screenshots: deep-link, multi-page sets, and authenticated pages.
  • Provisioning: --ensure-db (local Postgres + DB), --setup (non-blocking
    bring-up), mxcli init SessionStart hook, and a bootstrap prompt for web/iPad.

Docs

Local Dev Loop + Bootstrap Prompt pages, README Quick Start, and an install sweep
(raw mxcli-<os>-<arch> assets, recommend nightly, go install @latest doesn't
work — uncommitted ANTLR parser).

Proposals / research (from the earlier fork PR)

Warm-loop proposal + the check↔mxbuild diagnostics-catalog research and extractor.

Verified (Mendix 11.12.1)

boot → HTTP 200; page edit → re-bundle + reload (~4.7s, change visible in screenshot);
entity add → restart + DDL, 88 tables synced; --ensure-db provisions from scratch.
Pure logic unit-tested; JVM/process orchestration verified by manual E2E.

Follow-ups (not here)

Slice 3 (external preview), slice 4 (tunnel hub); go install support; JDK 9/10.

claude and others added 28 commits July 17, 2026 23:57
Draft proposal covering two scenarios: a Docker-free `mxcli dev` warm run
loop to replace the docker inner loop, and an iPad split-screen preview
workflow (Claude Code Web + browser, preview-before-commit). Built on
measured mxbuild --serve incremental builds (~0.8s warm vs ~30-60s
one-shot) and the runtime reload_model hot-reload primitive (~0.1s, no
restart), with restartRequired branching page/microflow from entity/DDL
changes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
Frame the iPad scenario as one leg of the full journey: Claude design
prototype -> Mendix model via mxcli/MDL -> production security
(ALTER PROJECT SECURITY LEVEL PRODUCTION, per-role grants) -> live
warm-loop testing under real security, all on iPad without Studio Pro.

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

Verified reload_model reloads model store + microflows + i18n only, not the
datastorage/entity layer. The runtime reconciles its metamodel catalog
(mendixsystem$entity/entityidentifier/attribute/association) at startup, so
entity/view-entity/association changes need a restart. Confirmed directly: a
view entity added via reload_model never appears in mendixsystem$entity.

Adds the two-signal decision matrix (restartRequired x get_ddl_commands) and
the key nuance that OQL view entities need restart WITHOUT DDL (query-time,
not materialized). Notes the standalone boot lacks preview_execute_oql for
functional OQL verification.

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

One static hub fronting many dynamic dev containers, verified via chisel:
- chisel fan-in (one server, many clients on distinct ports; --authfile isolation)
- why chisel works where ngrok/cloudflared/localtunnel didn't (go install past
  the gate, single-443 multiplex, system trust store already holds the MITM CA)
- mxcli tunnel-hub (static) + mxcli dev --hub (self-registering dynamic side)
- registration flow over chisel's authfile-reload + client-declared-remote primitives
- admin overview page: fleet list + per-container pending-change summaries
- Mendix fleet wrinkles (subdomain ApplicationRootUrl, cookie/DB isolation, slots)
- reclassify coupling Option D (live tunnel) from "not recommended" to verified/preferred
  for the multi-container dev loop, artifact-push kept as durable fallback

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

How a user goes from a new Claude Code Web project to a running testable app:
- entry points: mxcli new (new app) / clone + mxcli init (existing)
- gap identified: init scaffolds a devcontainer + .claude tooling but no
  SessionStart hook; Claude Code Web reaps Postgres/JVM on idle, so it needs an
  idempotent `mxcli dev up` wired to SessionStart
- dev up = setup mxcli/mxbuild/mxruntime (all exist) + Postgres + boot + serve
- first-run vs warm cost table; heavy costs are one-time and image-cacheable
- adds impl rows (dev up, init SessionStart hook) and an open question on
  image pre-baking + the SessionStart contract

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
Restructure Provisioning into three entry points (prompt template primary for
web/iPad, repo template, local CLI). Add the prompt-template subsection with:
- the draft bootstrap prompt
- rule 1: committing the config is mandatory (survives reaping; produces the
  repo-template end state without a maintained template)
- rule 2: mxcli delivery is an environment-layer concern (github gate blocks
  release curl; go install works only for a public module) - proven via chisel

Adds impl rows (new --emit-template, shipped prompt template, public-module/
env-preinstall delivery) and open question 12 on mxcli delivery into a gated
empty repo.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
Keep one proposal but show how to ship it incrementally as four independent,
value-ordered slices: (1) warm local loop, (2) provisioning, (3) single-app
external preview, (4) tunnel hub. Recommends 1->2 first (solo dev, no external
parts), 3 for single-app preview, 4 as a separately-reviewed scale-out.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
Ran scripts/gen_proposals_readme.py. Indexes 5 previously-missing active
proposals: mxcli_dev_warm_loop, check_mxbuild_gap_heuristics,
ai_capability_dataset, architecture_graph_visualization,
playwright_session_reuse.

Side effect, flagged: PROPOSAL_marketplace_modules (status: shipping) and
UNIFIED_SCHEMA_REGISTRY (status: descoped) drop from the index because their
statuses are non-canonical; the generator only buckets canonical statuses.
They remain on disk and re-index once their statuses are normalised
(maintainer's call — not changed here).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
Lint category-2 fix (maintainer-approved) so both re-appear in the index:
- PROPOSAL_marketplace_modules: status shipping -> partial (active); tidy date
- UNIFIED_SCHEMA_REGISTRY: status descoped -> abandoned; git mv to archive/
  (terminal status belongs in archive/; retired into engalar's codec)

Regenerated README via scripts/gen_proposals_readme.py: marketplace now under
In Progress, UNIFIED under Archived, warm-loop still indexed. No proposals
dropped from the index.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
Document the first-part research (mxcli check improvements) as a proposal:
- where Mendix's diagnostics catalog lives (Mendix.Modeler.Texts.dll PO files)
- structure: CODE__SUMMARY msgids, {param} templates, severity via CE/CW/CI
  prefix, LOCATION + MX_DOCS_CAT tags
- findings: 1318 codes (11.12.1), static-checkability tiering, mxcli already
  reimplements ~77-81, mx check is same-floor as a build (Go check is the only
  speedup)
- approach: versioned data asset, port by tier/frequency, harvest-against-oracle
- verbatim examples (small fair-use sample)

Commit scripts/extract-diagnostics-catalog.sh (the methodology). Its OUTPUT is
Mendix's proprietary corpus and is NOT committed (see IP open question).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
docs: add warm dev loop proposal (mxbuild --serve + reload_model)
… --local

First increment of the warm dev loop (PROPOSAL_mxcli_dev_warm_loop.md, slice 1):
a Go client for `mxbuild --serve`. StartServe launches the build server (reusing
resolveJDK21 + PrepareMxCommand's FreeType LD_PRELOAD), Build POSTs /build and
parses {status, restartRequired} — the signal for reload_model vs restart — and
Stop shuts it down.

- mxserve.go: ServeServer (StartServe/Build/Stop), BuildRequest/BuildResult.
- mxserve_test.go: 5 unit tests over the HTTP client (mocked endpoint).
- mxserve_integration_test.go: gated real end-to-end; measured cold 21.8s ->
  warm 1.6s incremental against the spike app.

Naming: this backs the chosen `mxcli run --local` command (wiring in a later
increment). Next: warm the roundtrip test harness (replace cold `mx check`).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
A serve (or one-shot) build fails Java compilation with a cryptic
"package com.mendix.* does not exist" when the mxbuild cache is missing its
sibling runtime/ dir (the Mendix API libraries on the javac classpath).
verifyMxBuildCache fails fast in StartServe with an actionable message instead.
Found while verifying --serve on 11.12.1 (cold 10.2s -> warm 1.1s once the
runtime/ dir was present).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
RuntimeController wraps the M2EE admin API for the standalone `run --local`
loop (increment 3 of the warm-dev-loop proposal). It is the novel, fully
unit-testable core of that command, separate from process launch and cobra
wiring which follow.

- ApplyBuild maps a serve build's restartRequired flag to the apply action:
  false -> hot reload_model (page/microflow/text change), true -> relaunch
  the runtime (entity/view/association change, which the runtime reconciles
  in its metamodel catalog only at startup) then run the start cycle.
- Start models the DB-aware start sequence discovered this session: start ->
  if the runtime reports the schema is out of date -> execute_ddl_commands ->
  start. DDL is discovered during the start cycle, not by a pre-check on the
  running (old-model) runtime.
- DecideApply and needsDBUpdate are pure and documented in one place.

This differs from docker's Reload(): that path warns on pending DDL and tells
the user to restart; the controller actually executes the DDL and drives the
restart, off the serve restartRequired signal.

Tests use an httptest mock admin that records the action sequence, asserting:
reload vs restart branch, the clean vs out-of-date start cycle, DDL failure
abort, status parsing, and the pure-function cases.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
localboot.go boots a Mendix runtime as a plain JVM (no Docker) for
`mxcli run --local`, using the boot recipe reverse-engineered this session:
launch runtimelauncher.jar with the M2EE/MX_INSTALL_PATH env, configure over
the admin API (update_appcontainer_configuration + update_configuration), then
run the RuntimeController start cycle.

Key detail: a standalone runtime does NOT apply design-time constant defaults,
so a missing constant 530s the app. mxbuild writes the resolved constants to
<deployDir>/model/config.json; readDeploymentConstants lifts them and feeds
them back as MicroflowConstants — so booting any mxbuild-generated app works
without hand-listing its constants.

LocalRuntime also exposes Restart() (re-launch + re-apply config, since config
is per-process) for use as the RuntimeController.ApplyBuild restart callback,
and Controller()/AppURL()/HealthOK()/Stop().

The pure config/env/path builders and the deployment-constants reader are
unit-tested; process launch is left to an integration path (mxbuild + JDK).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
Wires the serve client and standalone runtime boot into a single command: a
warm, Docker-free local dev loop. First build is cold (~10-15s); after that a
model change rebuilds incrementally via mxbuild --serve and is hot-applied,
with the serve build's restartRequired flag choosing reload vs restart.

RunLocal (cmd/mxcli/docker/runlocal.go):
- detects the project version, ensures mxbuild + runtime are cached, and links
  the runtime into the mxbuild cache so serve's javac resolves the Mendix API
  (avoids "package com.mendix.* does not exist");
- reuses an already-cached runtime instead of re-downloading (resolveRuntimeInstall);
- checks the database is reachable (Postgres; devcontainer-friendly defaults,
  db name derived from the project) with an actionable error;
- starts serve, does the first Deploy build, boots the runtime, and stays up;
- with --watch, polls the project (v1 and v2 layouts, deployment/ excluded) and
  rebuilds + hot-applies each change via RuntimeController.ApplyBuild.

Also resolves JDK 21 in StartLocalRuntime when JavaHome is unset.

cmd/mxcli/cmd_run.go adds the top-level `run` command with --local/--watch and
--app-port/--admin-port/--serve-port/--db-* flags.

Verified end-to-end against a blank Mendix 11.12.1 app on a clean Postgres:
boot -> HTTP 200; a microflow edit applies via reload in ~1.1s (no restart);
an entity add applies via restart in ~9s, runs DDL, and returns HTTP 200.

Pure helpers (defaults, db-name derivation, runtime linking, change signal)
are unit-tested; process/JVM orchestration is exercised by the manual E2E above.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
Adds user-facing docs for `mxcli run --local`:
- docs-site page tools/run-local.md (+ SUMMARY.md TOC entry);
- synced skill .claude/skills/mendix/run-local.md (how-to, apply-decision table,
  Postgres prereq, the edit -> hot-apply loop, validation checklist);
- CLAUDE.md implementation-status entry.

All three state the current limitation honestly: the runtime/model/API work and
the app answers HTTP 200, but the browser client renders blank because the web
client bundle (web/dist/index.js) is produced by a rollup step the serve path
does not run. Records the finding and the next step in the proposal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
The serve Deploy target writes the web client *source* (web/index.js,
web/pages/*, web/widgets/*, web/rollup.config.mjs) but not the rollup *bundle*
(web/dist/index.js) that index.html loads, so a standalone-served app 404'd on
its client bundle and rendered blank.

docker.BuildWebClient runs mxbuild's own bundled rollup runner
(modeler/tools/node/rollup-runner.mjs, NODE_ENV=production, one-shot) over
deployment/web to produce web/dist/. resolveNodeTooling locates the bundled node
+ runner from the mxbuild path, preferring the GOOS/GOARCH platform dir with a
glob fallback. RunLocal calls it after the initial build and after each --watch
rebuild.

Verified end to end: `mxcli run --local` on a blank 11.12.1 app now serves
/dist/index.js (HTTP 200), and driving the devcontainer Chromium with Playwright
renders the Mendix homepage fully.

Docs (site page, skill, CLAUDE.md, proposal) updated: pages now render;
the remaining item is making the --watch client rebuild incremental (a full
rollup bundle is ~6-7s today) via rollup's watch-mode companion bundler.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
Completes the pixel-perfect page loop for `mxcli run --local --watch`.

Incremental web client bundler (docker.WebClientWatcher):
- keeps mxbuild's rollup runner hot in watch mode (client-side mirror of
  `mxbuild --serve`), parsing its modern-web-bundler-protocol stdout to count
  successful bundles; a page/widget edit re-bundles in ~3-4s vs ~7s cold.
- runs with CHOKIDAR_USEPOLLING (+INTERVAL): inotify is silent on container
  overlay filesystems, so without polling change detection took tens of seconds;
  polling drops it to ~1s.
- the watch loop re-bundles only when the edit touched web/ client source
  (mtime gate); microflow/entity edits skip the bundle and just hot-reload.
  WaitForRebuild settles out cleanly if the touched file isn't a rollup input,
  so it never hangs. Non-watch mode keeps the deterministic one-shot bundle.

Playwright screenshots (docker.CaptureScreenshot, --screenshot):
- captures a PNG after boot and each applied change via Playwright's built-in
  `screenshot` command (Chromium from PLAYWRIGHT_BROWSERS_PATH; no playwright-cli
  dependency). --screenshot-path / --screenshot-url configure output and page.

Change signal fix: watch model source (.mpr + mprcontents/) only, not the whole
project dir. The build rewrites theme-cache/, .mendix-cache/, deployment/ every
run and screenshots land in .mxcli/; a whole-dir watcher self-triggered an
infinite rebuild loop.

Verified E2E on a blank 11.12.1 app: page edit -> incremental re-bundle ->
reload -> auto-screenshot showing the live change (~4.7s); microflow edit ->
reload, no re-bundle (~4s); no self-trigger loop while idle.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
Extends --screenshot for the pixel-perfect page loop on secured apps and
specific pages:

- --screenshot-url now accepts a page path resolved against the app root
  (e.g. /p/customers), or a full URL as-is, so a specific page under edit is
  shot rather than the app root (resolveScreenshotURL).
- --screenshot-user/--screenshot-password log in once through the Mendix login
  form and reuse the session for every screenshot, so pages behind login render
  authenticated. docker.LoginAndSaveStorage runs a headless Playwright script
  (driving #usernameInput/#passwordInput/#loginButton) and writes a Playwright
  storage state; CaptureScreenshot loads it via `screenshot --load-storage`.
  Login is best-effort: an anonymous app with no form proceeds unauthenticated.

The login script is run through the same Playwright install, resolved from the
`playwright` CLI's package dir (playwright-core via require.resolve) with a
system-node/mxbuild-node fallback — no hardcoded paths. It is written as .cjs so
Node treats it as CommonJS.

Verified E2E on the 11.12.1 app: login produces a storage state with the Mendix
session cookies, and the screenshot renders the authenticated page.

Docs (site page, skill, CLAUDE.md, proposal) updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
--screenshot-url is now repeatable: more than one target produces a screenshot
set — one PNG per page after every applied change — for a visual-regression
sheet across an app's key pages. Each PNG is named from the page slug
(run-local-p-customers.png, run-local-home.png; the app root -> -home).

ScreenshotURLs replaces the single ScreenshotURL; maybeScreenshot loops the
targets, deriving a distinct output name per page (screenshotOutName/slugifyPage)
when there is more than one, and keeps the single-file default otherwise. Auth
(storage state) and deep-link resolution apply to every page in the set.

Verified E2E: `--screenshot-url / --screenshot-url /index.html` produced two
distinct PNGs in one change cycle. Docs (site, skill, CLAUDE.md, proposal) updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
Starts slice 2 (provisioning) of the warm-loop proposal: a fresh session should
come up testable without a manual createdb. run --local previously only checked
DB reachability and errored; --ensure-db now provisions it.

docker.EnsureDatabase (PostgreSQL): no-op if the app can already connect;
otherwise, for a local host, starts the Postgres service if the port is down,
then creates the app role and database if missing via a local superuser
(sudo -u postgres psql). Remote hosts are only verified, never provisioned.
Identifiers are validated against a strict pg-identifier regex and the role
password is single-quote-escaped before any DDL, so names/passwords can't inject.

Also documents the slice-1 command-name drift in the proposal: the warm loop
shipped as `mxcli run --local [--watch]`, not the envisioned `mxcli dev` tree
(reload/exec/status/stop folded into --watch), and marks slice 1 shipped +
slice 2's DB-provisioning piece done in the slices table.

Verified E2E: with Postgres stopped and the database dropped, `run --local
--ensure-db` started Postgres, created the database, booted the runtime, and the
runtime synced 88 tables into the fresh DB. Pure helpers (splitHostPort,
isLocalHost, quoteSQLString, identifier validation) are unit-tested.

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

Finishes slice 2 (provisioning): a fresh Claude Code Web session self-bootstraps,
and an empty repo can be seeded from a prompt — no GitHub template needed.

run --local --setup: a non-blocking bring-up. It does the idempotent
prerequisites (detect version, cache MxBuild+runtime, ensure the database with
--ensure-db) and RETURNS without booting serve/runtime — the long-lived loop
would block a SessionStart hook. The agent runs the full `run --local` on demand.

mxcli init: emits a Claude Code SessionStart hook into .claude/settings.json
(ensureSessionStartHook / addSessionStartHook) running
`test -x ./mxcli && ./mxcli run --local --setup --ensure-db -p <app.mpr> || true`.
The merge is generic over the JSON, so existing settings/hooks are preserved and
re-running init is idempotent (marker match); invalid existing JSON is left
untouched rather than clobbered. Verified E2E: the hook merged cleanly into the
settings.json init already writes (env + permissions retained).

Bootstrap prompt: docs-site/src/tools/bootstrap-prompt.md — the copy-paste seed
for an empty repo (web/iPad), using the shipped commands, with the two robustness
rules (commit the config; mxcli delivery is an environment concern). This is the
primary iPad path, chosen over a GitHub template repo (mobile shows only a small
template subset, and a template repo needs per-version upkeep).

Docs (site page + SUMMARY, skill, CLAUDE.md, proposal) updated; slices 1 and 2
marked shipped.

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

Adds a prominent Quick Start subsection ("From the web or an iPad") pointing at
the bootstrap prompt and the warm local dev loop (run --local --watch
--screenshot), so the empty-repo web/iPad path is discoverable from the README.

Documents which mxcli version each install path yields, in bootstrap-prompt.md:
- `go install ...@latest` -> latest tagged release (not nightly; pin with @vX.Y.Z)
- `mxcli setup mxcli` -> matches the running binary's version (nightly->nightly,
  vX.Y.Z->that release); override with --tag
- environment image pre-install -> whatever the image bakes (recommended for
  pinning a version fleet-wide)
Recommends pinning for a reproducible bootstrap.

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

Delivery-pipeline check findings:
- github.com/mendixlabs/mxcli IS a public Go module (tags v0.1.0–v0.16.0;
  @latest -> v0.16.0 per proxy.golang.org).
- release.yml (on v* tags) runs `make release` and uploads bin/mxcli-* via
  softprops/action-gh-release; make release builds mxcli-{linux,darwin,windows}-
  {amd64,arm64}(.exe), which exactly match what `mxcli setup mxcli` downloads.
  nightly.yml maintains a rolling `nightly` release. So the prebuilt-binary path
  is correct and consistent with setup mxcli's naming.
- BUT `go install …/cmd/mxcli@latest` does NOT work: the generated ANTLR parser
  (mdl/grammar/parser/) is a hard import yet is gitignored and not committed, so
  the tagged module source is missing the package and the build fails.

Fix bootstrap-prompt.md to stop recommending `go install @latest`: use the
prebuilt release binary (mxcli setup mxcli / direct mxcli-<os>-<arch> download)
or the environment pre-install; from-source builds need `make grammar`. Enabling
go install (commit the generated parser, or generate during module build) is
flagged as a maintainer decision.

(Could not verify the release assets are attached to the tags from this session —
GitHub is scoped to ako/mxcli, so api.github.com/github.com return proxy 403 for
mendixlabs/mxcli — but the release CI is present and correct.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
Nightly is already downloadable (`mxcli setup mxcli --tag nightly`, or the
`.../releases/download/nightly/mxcli-<os>-<arch>` asset). Recommend it for the
bootstrap while mxcli is fast-moving alpha: the warm-loop surface referenced by
the prompt (run --local, --watch, --ensure-db, --setup, screenshots) lands in
nightly before it reaches a tagged release, so the documented flow needs it.
Pinning a vX.Y.Z release remains the reproducibility/stability option, and the
go-install caveat stays.

Docs-only.

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

Swept all user-facing docs for install accuracy. Two real inaccuracies fixed
beyond the go-install issue:
- Release assets are RAW binaries named mxcli-<os>-<arch> (not mxcli_<os>_<arch>.
  tar.gz archives). quickstart.md and installation.md told users to download and
  `tar xzf` a .tar.gz that doesn't exist — replaced with a direct curl of the raw
  binary + chmod +x.
- `go install …@latest` was listed as "build from source"/an install option in
  quickstart.md and installation.md; it fails (uncommitted generated parser).
  Replaced with `git clone && make build`, plus a caveat note.

Also: recommend the rolling `nightly` build across README/quickstart/installation
while mxcli is fast-moving alpha (features land there before a tagged release),
with vX.Y.Z pinning for reproducibility. Flagged the go-install reality in the
warm-loop proposal's release row (it claimed a public Go module suffices).

Files: README.md, docs-site/src/tutorial/{quickstart,installation}.md,
docs-site/src/tools/bootstrap-prompt.md (earlier), PROPOSAL_mxcli_dev_warm_loop.md.
Docs-only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
@ako
ako merged commit ea98064 into mendixlabs:main Jul 19, 2026
4 checks passed
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