diff --git a/CHANGELOG.md b/CHANGELOG.md index 18ae550..776b56a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,13 @@ Credit the person or project that surfaced the change — the credit line is the - process(post-mortem): `process/post-mortem-inverted-evidence-2026-09-02.md` — the build session's own failure analysis of the VBA migration, anonymised: the evidence hierarchy was inverted (a restorable `.bak` recorded as "unrecoverable" for five months while the analysis was derived from code and from BRDs derived from that code), absence recorded as a finding three times, presence counted instead of content read three times in one session, an intake claim propagated as fact, gates checking artifacts not truth, architecture derived from the lossy artifact, a ~130 that measured 19. Six toolkit changes, one per failure, each citing it: (F1) `query-the-model.md` gains the **evidence hierarchy** — production data > configuration tables > client statements > source code > our own earlier conclusions — and `source-formats.tsv` routes `.bak`/`.mdf`/`.bacpac`/`.dmp` to "restore and query", ranked top; (F2) `brd-validation.md` **check 7**: a negative claim names what was searched and carries an expiry, retired ≠ absent, a number carries its query or is marked estimate; (F3) **the ledger's verdict is hand-assigned**: `mark` now requires `--evidence ""`, and a pattern-covered file the artifact never names is a FAULT, not EXTRACTED-unverified — on the motivating corpus that turns 33 of 95 modules from a quiet count into 33 blocking rows, which is the honest state; (F6) `source-triage.md` anti-patterns "reading the interpreter as the specification", "conformance for correctness", "skipping the backup"; (fix 5) `bin/open-questions.sh` prints **RE-CHECK** — every source that arrived after the questions were answered and has no disposition yet, with the count of ANSWERED/ASSUMED/RAISED questions open against it (`recheck` in `--json`); (fix 6) `improvement-register.md` gains **Corrections**: a superseded claim, the measurement that overturned it, and where the old claim still stands. Fixture `test-source-ledger.sh` grows to 36 assertions — VB-USI (DafNe track), the build session's post-mortem - new(source-ledger): `bin/source-ledger.sh` + `bin/lib/source-inventory.py` + `bin/lib/source-formats.tsv` — **every source file must name the artifact that consumed it, or carry a person's waiver, before Stage 1 or 2 passes.** The incident: on a VBA migration the intake closed "documents not yet accounted for?" with *"the .pptx was already used by the triage pass"* — a claim about another document nobody checked; the triage was derived from the `.cls` exports, and the deck's 22 diagrams (the only statement of the workflow engine's X/M/E semantics) went unread for two months of BRDs, blueprint and build plan, reversing a CONFIRMED decision when finally opened. Three fixes, one shape each: (1) the Stage 0 inventory is now EVERY file under the source root minus a denylist — the extension allowlist that hid the deck had already been patched for `.yaml` (08-19) and code (08-31), and an allowlist is only ever as complete as the last incident; Office/PDF rows carry their embedded image and page counts, `init --refresh` appends files that arrive later; (2) a per-row disposition the gate greps — the artifact must exist, be non-empty and *name* the file, and for a container account for its pages and images — with `gate-check.sh --waive source/ --reason` as the one way to skip a file (the user's "if I agree to skip, skip"); (3) drift: files on disk with no inventory row block by name. Blocks Stages 1–2 (a file test, not a conversation test, so it clears skills-over-scripts); advises at Stage 0. Field-run on the motivating project (96 files): the intake's own claim replays as FAULT "never names the deck", the first extraction pass (3 of 22 images) replays as FAULT, the second passes, and 33 of 95 VBA modules turn out to be named in no analysis file at all. Fixture `tests/wave2/test-source-ledger.sh`, 32 assertions. Skill text: `source-triage.md` (Core Principle + anti-patterns), `interview-protocol.md` ("an answer that points at another document is a claim"), `conversion-runbook.md` Stage 0/1 rows, `document-discovery.md` completion rule — VB-USI (DafNe track) ## 2026-09-08 +- fix(commands): `mobile-dev-loop-prompt.md` no longer claims the build gate needs Studio Pro — the gate is `mxbuild`, a plain binary `exec.sh` invokes directly, and its only Studio Pro coupling is the `.mpr` lock check, a no-op where no Studio Pro exists; both mobile prompts, `testing-shape.md` and `e2e-harness-base.md` now know `mxcli run --local / --hub / --test-endpoint`, so a container stands the app up itself and publishes it at a public URL instead of hand-rolling a tunnel, and the harness `BASE_URL` no longer assumes `localhost:8080` (cherry-pick of 372e2ac, consistent with the cloud lane in `cloud-dev-environment.md` and the runbook's "Where you run this") — Maurits Visser's Mac parking branch, salvaged by the toolkit merge queue +- new(skills/existing-app-change.md): the entry mode the three existing ones did not cover — changing a live Mendix app that has no BRDs, no architecture document and no wireframes. The knowledge base comes from the model itself (**Path D**, `SHOW`/`DESCRIBE`, which cannot silently return zero the way a code extractor does), stages 2–4 run over the changed slice plus its **blast radius** (associations, writers, bound pages, module tangles, published services — the Stage 0 question with no equivalent elsewhere), the Track B regression baseline is the precondition rather than an option, every gate from Stage 5 on is also a regression gate, and the ledger denominator is the slice's leaves, said out loud. Routed as `ba,architect / P,0 / ondemand / spine`; `existing-app-assurance.md` now points here instead of back at the runbook — Maurits Visser's Mac parking branch, salvaged by the toolkit merge queue +- learn(workflow): `learned-workflow-patterns.md` §21 — a full v0.20.0 re-probe (2026-08-31, Mendix 11.13.0) rebuilt a 23-activity BPM-engine conversion workflow from pure MDL at 0 native errors: non-interrupting boundary timer and `wait for timer` cleared, `$Type` re-confirmed fixed, `DESCRIBE WORKFLOW` reads decisions/jumps/waits/boundary events back, `create or replace workflow` now refuses to delete an Event Sub-Process; four gaps remain (Event Sub-Process, End activities, body annotations, a three-fix `describe → exec` loop) and the `JUMP TO` trap is spelled out — mxcli names activities after the called microflow, a dangling target is stored as a self-reference, and `CE6681` names the wrong fault. Merged against master's newer state: the `DECISION` result is recorded as **disputed**, not cleared, because the toolkit's own same-day retest reproduced BUG-76 byte for byte with `decision '1 = 1'`; §8's version table, §10 step 5 and the Notes on scope updated accordingly — Maurits Visser's Mac parking branch, salvaged by the toolkit merge queue +- learn(preflight): STOP #24 — a `JUMP TO` target must be a real activity name and mxcli will not check it (passes `check --references` and `exec`, stored as a jump pointing at itself, `CE6681` at native check; `DESCRIBE` output walks you into it); the v0.16.0 `WITH`-clause gotcha is stamped as v0.16.0-only after 14/14 calls stored correctly on v0.20.0 — Maurits Visser's Mac parking branch, salvaged by the toolkit merge queue +- new(bug-logs): BUG-140 — a full page regeneration never wires a parameterized microflow datasource on a top-level `dataview` (CE1571 on 15 of 15 pages, even byte-identical to a working original; explicit `Params:` is accepted and dropped, enum literals are rejected outright); only implicit binding through one level of nesting works, proven on a throwaway page. Number 140 assigned at merge, clear of the 127+ block another branch was taking. Plus addenda where the branch's findings matched existing entries instead of new numbers: BUG-109 gets the general dangling-`jump to` case and its upstream issue #1005, BUG-110 its #1006 link and the second emitter defect #1007 (`annotation` statements `MDL-WF04` rejects), BUG-118 an earlier v0.18.0 two-entry `ContentParams` observation from a second project and the full-page-rewrite workaround; a "Cleared on v0.20.0" section records the workflow re-probe, with BUG-76 marked disputed against the same-day retest — Maurits Visser's Mac parking branch, salvaged by the toolkit merge queue +- fix(checkpoints): the last three `project-profile.md` references — a file no project has — now point at `PROJECT.md` → `## Decisions` (`checkpoint-design.md` Figma/brand answers, `checkpoint-scope.md` external refs, `checkpoint-architecture.md` NFRs; the third was not on the branch, found by grep at merge) — Maurits Visser's Mac parking branch, salvaged by the toolkit merge queue +- process(CLAUDE.md): "Testing this toolkit" now says there are two suites — `tests/run-tests.sh` is self-contained, each `tests/wave2/test-*.sh` takes its subject as `$1` and several take something other than `gate-check.sh` — so a permitted single-fixture run is invoked correctly the first time (checked still accurate: 24 fixtures, `run-all.sh` resolves the subject from the same `usage:` line) — Maurits Visser's Mac parking branch, salvaged by the toolkit merge queue - fix(gate-check): the source-ledger block for Stages 1–2 now respects a stage declared not required — `--adopt` at a later stage, `Waived stage N`, or an entry mode that skips it — instead of blocking before the verdict table was consulted (obligation and artifact checks already did); and the `source-ledger` manifest row's absence class is `report`, not `gate`, because nothing mandatory produces `analysis/source-ledger.html` (gate-check runs `check`, never `report`) and a row claiming to hold a gate shut over a file no step writes would sit PENDING on every migration project — the toolkit merge queue, reviewing the intake-gates branch - new(gate-check): `--closeout ` — the stage close-out / next-stage-open block (`bin/lib/closeout.sh`), generated from the register, the artifact manifest and the routing table, pasted as-is at every gate (runbook §1b rule 7; CAC-3 and CAC-5 open with it): artifacts produced, decisions made in the stage (first 15, every non-CONFIRMED row always), carried forward (open questions with the status word that keeps them open, ASSUMED rows, UNSYNCED markers), the gate line verbatim **and in plain words**, then what the next stage does, how it is worked and tested, its checkpoints, the top-5 governing skills from `skill-routing.tsv`, and the optional artifacts on offer. Field run on TFC-TCXGraphPOC Stages 2–4: surfaced a PENDING coverage ledger nothing had flagged, 2 UNSYNCED markers, 5 open questions with status words outside the closed vocabulary ("TO", "REVERSED", "PARKED"), and the hand-written `architecture/workflow-definition.md` as an opt-in-by-deed; fixture `tests/wave2/test-closeout.sh` (32 assertions) — TFC-TCXGraphPOC - new(gate-check): plain-words paragraph under every stage-query outcome (PASS, PENDING, FAIL, MANUAL, drift-blocked) — status in one sentence, what the gate needs, why specifically, what is still missing in plain names; asked for as "explain why a gate is blocked or what is pending, in human-understandable language" — TFC-TCXGraphPOC diff --git a/CLAUDE.md b/CLAUDE.md index 75189ab..afc2705 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -250,6 +250,13 @@ changed `bin/gate-check.sh` → also run `test-bug03-gates.sh`, `test-source-suf whatever else targets it). Grep `tests/wave2/` for the filename you changed if you're unsure which fixtures cover it. +There are two separate suites. `tests/run-tests.sh [-v]` is self-contained (guard-script +fixtures with no external argument). Each `tests/wave2/test-*.sh` fixture instead takes its +subject-under-test as `$1` — read its own `usage:` comment rather than assuming a fixed target; +several take something other than `gate-check.sh` (`exec.sh`, `sync-project.sh`, +`check-docs-numbering.sh`, a skill file). Once you have permission for a single fixture (see +below), run it as e.g. `bash tests/wave2/test-bug02-register.sh bin/gate-check.sh`. + **Why:** this repo is routinely edited by more than one agent session at once (real incident, 2026-08-19 — a peer session mid-edit reported known, unrelated failures already present in the shared working tree). A full-suite run over a moving target mixes someone else's in-flight diff --git a/README.md b/README.md index 5a67e4d..d1f4d5c 100644 --- a/README.md +++ b/README.md @@ -494,6 +494,7 @@ Every mxcli project has a `.ai-context/skills/` directory (bundled by `mxcli ini | CAC-6, after Stage 6 passes and before any cutover step — migration mode only, and a hard gate: every answer lands CONFIRMED, no ASSUMED defaults | `skills/checkpoints/checkpoint-cutover.md` | | Generating a new project's CLAUDE.md — baseline routing plus project-specific facts | `skills/bootstrap-project.md` | | Setting up or resuming an mxcli project in a cloud/ephemeral container — the one-time setup order (mxcli download → mxcli init → init-project.sh → sources decision → push) and the commit-and-push loop that survives container reclaim | `skills/cloud-dev-environment.md` | +| Changing an EXISTING Mendix app — adding a feature, altering a flow, restructuring a module — when it has no BRDs, no architecture doc and no wireframes: the knowledge base comes from the live model (Path D), stages 2–4 run over the changed slice plus its blast radius only, and the Track B regression baseline is the precondition; audit-only stays in existing-app-assurance | `skills/existing-app-change.md` | | Cutover and retrospective — promoting proven patterns back into the toolkit | `skills/close-the-loop.md` | | Before citing ANY behavioural claim about the harness, the Mendix runtime or a test tool as evidence — a claim not in the register may not be cited | `skills/measured-claims.md` | | Any review pass that runs more than once — module-review, coherence, monkey, wiring-sweep: findings accumulate across runs, a per-run report cannot show a trend | `skills/improvement-register.md` | diff --git a/ROUTING.md b/ROUTING.md index 98b798a..079e94e 100644 --- a/ROUTING.md +++ b/ROUTING.md @@ -50,6 +50,7 @@ picks the row up. That is the whole procedure — there is no second list to rem | Deciding who answers a question — before putting any batch to the user. gap/conflict/choice/user-only is what keeps a gate batch at four questions instead of 127 | `bin/question-kinds.sh` | ba | 1,2,3 | baseline | | Generating a new project's CLAUDE.md — baseline routing plus project-specific facts | `skills/bootstrap-project.md` | ba | P | ondemand | | Setting up or resuming an mxcli project in a cloud/ephemeral container — the one-time setup order (mxcli download → mxcli init → init-project.sh → sources decision → push) and the commit-and-push loop that survives container reclaim | `skills/cloud-dev-environment.md` | all | P | ondemand | +| Changing an EXISTING Mendix app — adding a feature, altering a flow, restructuring a module — when it has no BRDs, no architecture doc and no wireframes: the knowledge base comes from the live model (Path D), stages 2–4 run over the changed slice plus its blast radius only, and the Track B regression baseline is the precondition; audit-only stays in existing-app-assurance | `skills/existing-app-change.md` | ba,architect | P,0 | ondemand | | Cutover and retrospective — promoting proven patterns back into the toolkit | `skills/close-the-loop.md` | all | 7 | ondemand | | Before citing ANY behavioural claim about the harness, the Mendix runtime or a test tool as evidence — a claim not in the register may not be cited | `skills/measured-claims.md` | all | - | ondemand | | Any review pass that runs more than once — module-review, coherence, monkey, wiring-sweep: findings accumulate across runs, a per-run report cannot show a trend | `skills/improvement-register.md` | mdl,gate,test,review | 5,6 | ondemand | diff --git a/agents/architect-agent.md b/agents/architect-agent.md index 67d188f..3de9fe7 100644 --- a/agents/architect-agent.md +++ b/agents/architect-agent.md @@ -46,6 +46,7 @@ You own architecture and build-plan decisions for {{PROJECT}}. Hard rule: you ne | `skills/checkpoints/checkpoint-build.md` | CAC-5, after design sign-off and before the build plan — build order and slice boundaries. Opens with a brainstorm | | `project-bin/check-design-portability.sh` | Before porting ds.css into SCSS, and at the Stage-3 gate — greps the stylesheet for rules that cannot match the HTML Mendix emits (rem against the real root, table/th/td selectors, positional row selectors). mx check, mxcli check and mxcli lint are all blind to CSS | | `skills/cloud-dev-environment.md` | Setting up or resuming an mxcli project in a cloud/ephemeral container — the one-time setup order (mxcli download → mxcli init → init-project.sh → sources decision → push) and the commit-and-push loop that survives container reclaim | +| `skills/existing-app-change.md` | Changing an EXISTING Mendix app — adding a feature, altering a flow, restructuring a module — when it has no BRDs, no architecture doc and no wireframes: the knowledge base comes from the live model (Path D), stages 2–4 run over the changed slice plus its blast radius only, and the Track B regression baseline is the precondition; audit-only stays in existing-app-assurance | | `skills/architecture-blueprint.md` | Diagramming target architecture — module defs, wiring, fit-gap, marketplace, security, NFRs, integrations | | `skills/modularize-domain.md` | Deciding module boundaries before "create module" | | `skills/design-artifacts.md` | Designing the brand and ONE ANNOTATED WIREFRAME PER SCREEN before building pages — the design system alone is half the deliverable | diff --git a/agents/ba-agent.md b/agents/ba-agent.md index 605ec59..02f1df1 100644 --- a/agents/ba-agent.md +++ b/agents/ba-agent.md @@ -59,6 +59,7 @@ You run discovery and the interview gates for {{PROJECT}}. You never touch the ` | `bin/brd-report.sh` | Reviewing what the BRDs actually say — the Stage 2 surface, for BRDs from any source. Reads every knowledge base at once, and keeps a section that is absent-because-not-applicable apart from one that is absent-because-expected | | `skills/bootstrap-project.md` | Generating a new project's CLAUDE.md — baseline routing plus project-specific facts | | `skills/cloud-dev-environment.md` | Setting up or resuming an mxcli project in a cloud/ephemeral container — the one-time setup order (mxcli download → mxcli init → init-project.sh → sources decision → push) and the commit-and-push loop that survives container reclaim | +| `skills/existing-app-change.md` | Changing an EXISTING Mendix app — adding a feature, altering a flow, restructuring a module — when it has no BRDs, no architecture doc and no wireframes: the knowledge base comes from the live model (Path D), stages 2–4 run over the changed slice plus its blast radius only, and the Track B regression baseline is the precondition; audit-only stays in existing-app-assurance | | `skills/assess-migration.md` | Assessing or planning a migration up front, before any pipeline is chosen | | `skills/migration-pipeline.md` | Running the extraction pipeline | | `skills/migrate-general.md` | Migrating from a stack that has no dedicated pipeline | diff --git a/bin/gate-check.sh b/bin/gate-check.sh index c630f11..2155e68 100755 --- a/bin/gate-check.sh +++ b/bin/gate-check.sh @@ -1478,8 +1478,8 @@ stage_protocol_paths() { # every stage as "(unmapped)" — noisy by design, but only after someone hits it. case "$1" in # - P) echo "skills/interview-protocol.md skills/grill-mode.md skills/agent-roles.md skills/bootstrap-project.md skills/cloud-dev-environment.md skills/mendix-epics-api.md skills/corpus-extraction-integrity.md skills/platform-link.md" ;; - 0) echo "skills/interview-protocol.md skills/grill-mode.md skills/checkpoints/checkpoint-template.md skills/checkpoints/checkpoint-scope.md skills/source-triage.md skills/assess-migration.md skills/migration-pipeline.md skills/migrate-general.md skills/migrate-outsystems.md skills/source-os11.md skills/os-xml-schema.md skills/source-node-express-react.md skills/document-discovery.md skills/extractor-quality-loop.md skills/qa-loop-goal-pattern.md skills/mendix-epics-api.md skills/corpus-extraction-integrity.md skills/gate-check-file-locations.md" ;; + P) echo "skills/interview-protocol.md skills/grill-mode.md skills/agent-roles.md skills/bootstrap-project.md skills/cloud-dev-environment.md skills/existing-app-change.md skills/mendix-epics-api.md skills/corpus-extraction-integrity.md skills/platform-link.md" ;; + 0) echo "skills/interview-protocol.md skills/grill-mode.md skills/checkpoints/checkpoint-template.md skills/checkpoints/checkpoint-scope.md skills/source-triage.md skills/existing-app-change.md skills/assess-migration.md skills/migration-pipeline.md skills/migrate-general.md skills/migrate-outsystems.md skills/source-os11.md skills/os-xml-schema.md skills/source-node-express-react.md skills/document-discovery.md skills/extractor-quality-loop.md skills/qa-loop-goal-pattern.md skills/mendix-epics-api.md skills/corpus-extraction-integrity.md skills/gate-check-file-locations.md" ;; 1) echo "skills/interview-protocol.md skills/grill-mode.md skills/checkpoints/checkpoint-template.md skills/checkpoints/checkpoint-extraction.md skills/migration-pipeline.md skills/source-os11.md skills/os-xml-schema.md skills/source-node-express-react.md skills/document-discovery.md skills/extractor-quality-loop.md skills/kb-generation.md skills/corpus-extraction-integrity.md" ;; 2) echo "skills/interview-protocol.md skills/grill-mode.md skills/checkpoints/checkpoint-template.md skills/checkpoints/checkpoint-brd.md skills/checkpoints/checkpoint-architecture.md skills/kb-generation.md skills/brd-generation.md skills/brd-validation.md" ;; 3) echo "skills/interview-protocol.md skills/grill-mode.md skills/checkpoints/checkpoint-template.md skills/checkpoints/checkpoint-design.md skills/architecture-blueprint.md skills/modularize-domain.md skills/design-artifacts.md skills/brd-to-build-plan.md skills/workflow-structure-rules.md skills/learned-mdl-cannot-express.md" ;; diff --git a/bin/lib/skill-routing.tsv b/bin/lib/skill-routing.tsv index d72295e..6e35b2d 100644 --- a/bin/lib/skill-routing.tsv +++ b/bin/lib/skill-routing.tsv @@ -101,6 +101,7 @@ mxcli-bugs bug-logs/mxcli-bugs.md A CE error or behavior that looks like a known bootstrap-project skills/bootstrap-project.md Generating a new project's CLAUDE.md — baseline routing plus project-specific facts ba P ondemand spine cloud-dev-environment skills/cloud-dev-environment.md Setting up or resuming an mxcli project in a cloud/ephemeral container — the one-time setup order (mxcli download → mxcli init → init-project.sh → sources decision → push) and the commit-and-push loop that survives container reclaim all P ondemand spine existing-app-assurance skills/existing-app-assurance.md Auditing or regression/e2e-testing an EXISTING app — no intake, no stages, no gates test,review 6 ondemand verify +existing-app-change skills/existing-app-change.md Changing an EXISTING Mendix app — adding a feature, altering a flow, restructuring a module — when it has no BRDs, no architecture doc and no wireframes: the knowledge base comes from the live model (Path D), stages 2–4 run over the changed slice plus its blast radius only, and the Track B regression baseline is the precondition; audit-only stays in existing-app-assurance ba,architect P,0 ondemand spine assess-migration skills/assess-migration.md Assessing or planning a migration up front, before any pipeline is chosen ba 0 ondemand source migration-pipeline skills/migration-pipeline.md Running the extraction pipeline ba 0,1 ondemand source migrate-general skills/migrate-general.md Migrating from a stack that has no dedicated pipeline ba 0 ondemand source diff --git a/bug-logs/mxcli-bugs.md b/bug-logs/mxcli-bugs.md index 542081e..4152edb 100644 --- a/bug-logs/mxcli-bugs.md +++ b/bug-logs/mxcli-bugs.md @@ -2744,6 +2744,15 @@ before touching the live one. > **CONFIRMED STILL OPEN on v0.20.0 — CRITICAL, verified 2026-08-31 with the byte-exact original signature: `StorageLoadException … The text 'OutcomeA' is not a valid EnumerationValueIdentifier`, project unloadable by mxbuild. Keep the STOP rule: no DECISION activities in CREATE WORKFLOW via mxcli.** See [mxlabs-v0.20.0-retest-2026-08-31.md](mxlabs-v0.20.0-retest-2026-08-31.md). +> **Contradicting evidence, same day (2026-08-31, v0.20.0, Mendix 11.13.0):** a separate probe rebuilt a +> real 23-activity workflow containing one `DECISION` from pure MDL and it **loaded natively at 0 +> errors** (`ExclusiveSplitActivity` stored, `learned-workflow-patterns.md` §21). That probe did not +> record the decision's expression/outcome shape, while the retest above used `decision '1 = 1'` with +> fresh outcome labels. Two v0.20.0 probes, opposite verdicts → the defect is **shape-dependent and +> not isolated**. The STOP rule stays; the next person with a v0.20.0 binary should bisect the shape +> (typed-attribute expression vs literal; outcome labels that match an enumeration vs invented ones). +> See "Cleared on v0.20.0" at the end of this file. + **Project:** PROJECT-A, Phase 15 (Approval native Workflow build), script 65 (`Approval.ApprovalWorkflow`). **mxcli version:** v0.17.0 (`2026-08-10T05:12:17Z`). @@ -3867,6 +3876,23 @@ expressed correctly in MDL. Mendix requires its path to end in *End workflow* or `END OF BOUNDARY EVENT PATH` are all parse errors as statements, and `JUMP TO` is this bug. Non-interrupting boundary events are unaffected and work correctly. +**Also observed — the general case, 2026-08-31, v0.20.0 (`2026-08-28T13:22:53Z`), Mendix +11.13.0, filed as [mxcli#1005](https://github.com/mendixlabs/mxcli/issues/1005):** a +`jump to X` whose `X` matches **no activity at all** — anywhere in the body, not only inside a +boundary event — passes `check --references` and `exec`, and is stored as a `JumpToActivity` +whose own `Name` is `X` with `TargetActivity` also `X`: a jump targeting itself. Native +`mx check` then reports `CE6681 "It is not possible to jump to end activities or jump-to +activities."` — the wrong fault (jump legality) for the real one (unresolved reference); the +same code is Mendix's genuine verdict on a *forward* jump (`workflow-structure-rules.md`), so +check the target exists before reasoning about direction. Write-path, confirmed by `strings -n 3` +on the stored `.mxunit`, not by `DESCRIBE`. It is easy to hit because **mxcli names a +call-microflow activity after the microflow it calls** (`SUB_CheckPackageAvailability`, never +`callMicroflow6`), the grammar cannot name one explicitly, and `DESCRIBE WORKFLOW` emits jump +targets using the *source* model's names — so `describe → exec` reliably produces a dangling +target. Workaround: repoint every `jump to` at a real activity name before exec and gate on +native `mx check`; `learned-mdl-preflight.md` STOP #24. Same entry, two shapes — do not file +the general case separately from #1005. + --- ## BUG-110: `DESCRIBE WORKFLOW` emits MDL it cannot re-parse when a targeting XPath contains quotes @@ -3899,6 +3925,19 @@ Feeding that back to `mxcli check` fails: current state before editing (`query-the-model.md`), so this silently produces a script that looks authoritative and cannot run. +**Filed:** [mxcli#1006](https://github.com/mendixlabs/mxcli/issues/1006) (2026-08-31, from an +earlier v0.20.0 / Mendix 11.13.0 observation of the same emitter defect — the draft in +`pending-github-issues/` already records the dedupe). **Also observed, same probe, second +emitter defect filed separately as [mxcli#1007](https://github.com/mendixlabs/mxcli/issues/1007) +because the fix lives elsewhere:** `DESCRIBE WORKFLOW` emits canvas annotations as `annotation` +statements that mxcli's own checker rejects with `MDL-WF04` — a real 23-activity workflow +produced 13 of them from unmodified describe output. Requested fix: emit them as MDL comments, +which is MDL-WF04's own remediation advice. Same family as #619 (unquoted reserved-word +identifiers) and #978 (`DESCRIBE PAGE` rejected by mxcli's own check) — the emitter is not held +to the parser's grammar by any test. Practical consequence: `describe → exec` needs three hand +fixes before it runs — double the XPath quotes, strip the annotations, repoint the jumps +(BUG-109 / #1005). The ritual is `learned-workflow-patterns.md` §21. + --- ## BUG-111: `mxcli syntax` drill-down rejects the example printed in its own help text @@ -4577,6 +4616,19 @@ template fresh, has correctly-bound ContentParams on its siblings — only a REP GALLERY template), confirmed via a real `mx check` CE0402 build error (not a silent drop — the gate caught it) plus `DESCRIBE PAGE`'s literal `` output. +**Earlier observation of the same defect, 2026-08-21, mxcli v0.18.0, Mendix 11.13.0, an +approval-workflow conversion project (a second project — corroboration, not a duplicate):** a +`REPLACE` inside a gallery template introducing a `dynamictext` with a **two-entry** +`ContentParams: [{1} = Attr1, {2} = Attr2]`, a genuinely distinct widget name (ruling out +BUG-08's duplicate-name case) and bare attribute names (ruling out BUG-23's `$currentObject/` +case) — `check --references` clean, exec "Altered page", `DESCRIBE PAGE` shows both entries +``, native `mx check` CE0402 once per entry. So the defect is not limited to single-entry +arrays, and it predates v0.20.0. Second workaround, for when there is no already-bound widget to +`SET` on: `DESCRIBE PAGE` the whole page, patch only the target widget block, and re-apply it as +`create or replace page` (swap the `create or modify page` keyword describe emits), then gate on +native `mx check` — the full-page writer binds template-scoped `ContentParams` correctly; only the +`REPLACE` path does not. + --- ## BUG-119: the bundled `write-nanoflows.md` skill teaches a `SHOW MESSAGE` grammar the shipped binary rejects, and `mxcli syntax` documents the activity nowhere @@ -5224,6 +5276,110 @@ quietly drop one. theme — the `ALTER PAGE` property surface disagrees with the `CREATE` one — but the failure mode is the opposite and much more dangerous. +--- + +## BUG-140: full page regeneration never wires a parameterized microflow datasource on a top-level `dataview` — only implicit binding through nesting works + +*(number assigned at merge — 140 taken deliberately, clear of the 127+ block another branch was +numbering the same day)* + +**Severity:** High — silent; `mxcli check --references` and the exec's own "Created page" both pass, only native `mx check` (CE1571) catches it +**mxcli version when found:** v0.18.0 (open as of v0.18.0; not yet retested on v0.20.0) +**Mendix version:** 11.13.0 +**Discovered:** 2026-08-21, an approval-workflow conversion project — 15 native-Workflow "station task" pages, each needing a dataview scoped to a lookup microflow keyed by the page's `WorkflowUserTask` parameter plus a per-page enum literal +**Reproducible:** yes — a full 15-page rebuild without nesting produced `CE1571` on all 15; a disposable throwaway page isolated nesting as the fix + +### Summary + +A `dataview` whose `DataSource: microflow X` requires **any** parameter cannot have that +parameter wired by a full `create or modify page` regeneration, when the dataview is a direct +child of the page body (not nested inside another dataview/snippet). Confirmed even when the +page is reproduced **byte-for-byte identical to its own pre-existing, working original** — no +edits at all — native `mx check` still throws `CE1571 "No argument has been selected for +parameter '...' and no default is available."` for every such parameter. `mxcli check +--references` and the exec's own "Created page ..." success message both stay silent; this is +caught only by a real native `mx check` (or Studio Pro's own error pane). + +### What does NOT fix it + +- Adding an explicit `Params: { Param: $value }` clause on the dataview: `mxcli check` accepts + this syntactically (no error), but the writer still drops the mapping — native check still + fails identically. +- Using an enum literal as a `Params:` value (e.g. `Params: { StationKey: + Approval.StationKey.WFST010 }`): rejected outright by `mxcli check` itself + (`mismatched input 'Approval' expecting VARIABLE`) — `Params:` accepts only `$variable` + references, never literals, on any binding. +- Reducing the microflow to a single parameter, while keeping the dataview a direct child of + the page body: still fails. Parameter *count* is not the variable — nesting is. + +### What does fix it — implicit binding through nesting + +The writer *can* wire a microflow-datasource dataview's parameter, but only when the dataview +is nested one level inside another dataview/snippet whose own current-object type exactly +matches the microflow's sole parameter type, and the inner dataview has **no** `Params:` clause +at all: + +``` +dataview dvOuter (DataSource: $PageParam) { + dataview dvInner (DataSource: microflow Module.SingleParamMicroflow) { + -- $currentObject here is whatever SingleParamMicroflow returns + } +} +``` + +`SingleParamMicroflow` must take exactly one parameter, of the same type as `$PageParam` +(or whatever the outer dataview's current-object type is). If the real business logic needs +more inputs than that one type provides (e.g. an enum literal that varies per page instance), +write a thin single-parameter wrapper microflow that hardcodes the rest internally via a normal +`call microflow` expression — enum literals *are* valid inside a microflow body, just never +inside a page's `Params:` clause. + +Verify the mechanism on a disposable throwaway page first (create it, native `mx check`, then +`DROP PAGE` it) before rolling out to real pages — a clean throwaway page next to N still-broken +real ones in the same `mx check` run isolates the fix from everything else in flight. + +### Relationship to other bugs + +Same failure signature and root category as BUG-95 (`show_page` action ignoring the named +variable, defaulting to `$currentObject`) and the "snippetcall doesn't auto-infer Params on +full regen" / "`ALTER PAGE REPLACE` silently unbinds `Attribute:` shorthand" findings in +`skills/learned-datagrid-customcontent-binding.md` — all are instances of mxcli's writer +silently failing to wire a parameter/argument mapping that Studio Pro's own GUI always forces +the user to complete, while `mxcli check` has no way to see the gap. Distinct from BUG-56 +(DataGrid2 *datasource* parameterized-microflow binding, resolved v0.17.0, archived) — this is a +plain `dataview`, not a DataGrid2 grid. + +### Workaround + +Use the nesting pattern above. Do not attempt a third variation of the `Params:` clause on a +non-nested dataview — the defect is structural (a missing implicit-binding pass for top-level +dataviews), not a syntax problem. + +--- + +## Cleared on v0.20.0 — a workflow re-probe, 2026-08-31 + +**Probed 2026-08-31** on mxcli v0.20.0 (`2026-08-28T13:22:53Z`), Mendix 11.13.0, against a real +23-activity conversion workflow rebuilt from pure MDL in a throwaway clone, gated with native +`mx check` via `mxcli docker check`. Result: **0 errors**. Full write-up: `learned-workflow-patterns.md` +§21. This is a second v0.20.0 data point beside `mxlabs-v0.20.0-retest-2026-08-31.md`, and on one +row the two disagree. + +| Defect | Entry | Status on v0.20.0 | +|---|---|---| +| `DECISION` corrupts the `.mpr` | BUG-76 | **Disputed.** This probe's `ExclusiveSplitActivity` stored and natively loaded; the same-day retest reproduced the byte-exact corruption with `decision '1 = 1'`. Shape not isolated — STOP rule stays (note under BUG-76). | +| MDL-written non-interrupting `BOUNDARY EVENT … TIMER` always malformed (`CE0105`) | a project-local finding, no toolkit entry | **Fixed for the non-interrupting form.** Timer wrote and loaded, reading a context attribute. The interrupting form is still unusable — BUG-109 and `learned-workflow-patterns.md` §19. | +| pre-11.9 `Workflows$CallMicroflowTask` `$Type` | BUG-WF06 (archived, fixed v0.17.0) | **Re-confirmed fixed.** 14/14 stored as `CallMicroflowActivity`. | + +Also new in v0.20.0: `create or replace workflow` now **refuses** when the target contains an +Event Sub-Process ("MDL cannot express one — rewriting the workflow would delete it"), instead +of silently destroying it. Do not read that guard's absence as safety on an older binary. + +Still unfixed: no branch-ending vocabulary — `end workflow activity`, `end activity`, `end`, +`terminate`, `stop`, `end workflow instance` all fail to parse, so End activities on +fall-through outcome arms remain a permanent hand edit (`workflow-structure-rules.md` §11). +Found in the same probe and filed: BUG-109's general dangling-`jump to` case (#1005) and +BUG-110's two emitter defects (#1006, #1007). ## BUG-127: `ALTER PAGE … REPLACE` of a pluggable widget silently drops properties you wrote — a Combobox comes back with no `Attribute` and still renders **Severity:** High — the widget draws normally and binds to nothing; only `DESCRIBE PAGE` shows it diff --git a/commands/mobile-auto-test-prompt.md b/commands/mobile-auto-test-prompt.md index bafe63a..9834910 100644 --- a/commands/mobile-auto-test-prompt.md +++ b/commands/mobile-auto-test-prompt.md @@ -14,6 +14,14 @@ it has no filesystem in common with your laptop and cannot reach `localhost` on exists so that gap gets stated up front, once, instead of being rediscovered as a confusing `fault` deep into a run. +**Updated 2026-08-21 — the container can now stand the app up itself.** This file used to present +a laptop-hosted app plus a hand-rolled ngrok/cloudflared tunnel as the only options. It is no +longer the shortest path: given the project's repo, this session can run the app locally with +`mxcli docker run` or `mxcli run --local`, and publish it with `mxcli run --local --hub` (a public +URL via `mxcli tunnel-hub`, with `ApplicationRootUrl` set correctly for that origin). Option (c) +below is now the default; (a) and (b) remain valid when you specifically want to test an app +instance that already exists elsewhere. + --- ``` @@ -28,12 +36,17 @@ Setup (do this first, in order): 2. From the toolkit clone, run `bin/doctor.sh` and fix anything it reports missing before continuing — it names exactly what's absent (Python, mxcli binary, etc.) and how to get it. 3. This app must be RUNNING and reachable from this container, plus its database reachable - for OQL/DB assertions. This container cannot reach a laptop's localhost. Either: - (a) it's deployed at a reachable URL — set APP_URL to that, and PG_HOST/PG_PORT (or the - M2EE admin port) to the reachable DB, or - (b) tunnel the local instance out (ngrok/cloudflared) and set APP_URL/PG_HOST to the - tunnel address: . - If neither is possible, stop and say so rather than guessing — a runtime instrument that + for OQL/DB assertions. This container cannot reach a laptop's localhost. In order of + preference: + (c) **stand it up here** — `mxcli docker run -p .mpr --wait`, or `mxcli run --local` + for a Docker-free warm loop (needs JDK 21 and a reachable PostgreSQL whose database + already exists). Add `--hub` if I need to click through it from a browser myself. + Verify with `mxcli docker status`; never infer "up" from the absence of an error. + (a) it's already deployed at a reachable URL — set APP_URL to that, and PG_HOST/PG_PORT (or + the M2EE admin port) to the reachable DB, or + (b) tunnel an existing local instance out and set APP_URL/PG_HOST to the tunnel address: + . + If none is possible, stop and say so rather than guessing — a runtime instrument that can't reach the app should report `fault`/`INVALID`, never be skipped silently or faked green. Skill to follow: skills/existing-app-assurance.md — Track B (Regression / e2e test net). diff --git a/commands/mobile-dev-loop-prompt.md b/commands/mobile-dev-loop-prompt.md index 887c9f1..77199dd 100644 --- a/commands/mobile-dev-loop-prompt.md +++ b/commands/mobile-dev-loop-prompt.md @@ -1,25 +1,30 @@ --- -description: Self-contained prompt to paste into a fresh Claude Code (mobile/cloud) session to run the drafting/static-check half of iterative-build-loop.md — it stops at the Studio Pro-dependent gate rather than faking it +description: Self-contained prompt to paste into a fresh Claude Code (mobile/cloud) session to run the drafting/static-check half of iterative-build-loop.md against an existing project repo --- Copy everything below the line into a new Claude Code session that has no access to your local machine. Fill in the `<...>` blanks first. -**Why this is scoped the way it is:** unlike a pure e2e test run, the build loop's gate needs -**Studio Pro** — a Windows/macOS GUI app — open and reachable at several mandatory points (close -SP → exec → mxbuild gate → reopen SP → Run Locally). The toolkit's own SP automation -(`save-sp.sh`, `restart-sp.sh`) is macOS-only (`osascript`/`lsof`/`open -a`). A headless cloud -container cannot drive a GUI app it has no display for, and there is no tunnel-equivalent fix for -that the way there is for a running web app. So this prompt deliberately does the half of the -loop that has no GUI dependency, and stops cleanly at the handoff instead of pretending to -complete the rest. +**Why this is scoped the way it is.** This prompt is the *drafting* half of the build loop, for +when you want MDL written and statically checked against a project repo without standing up a +runtime. That is a scope choice, not a platform limit. + +> **Correction, 2026-08-21 — this file previously claimed the build gate requires Studio Pro. It +> does not, and that claim blocked cloud sessions at a boundary that isn't real.** The gate is +> `mxbuild`, a plain binary that `project-bin/exec.sh` invokes directly; `exec.sh`'s only Studio +> Pro coupling is a *lock check* (refuse to write while SP holds the `.mpr`), which is a no-op +> where no SP exists. `mxcli new` creates a project headlessly, `mxcli docker run` or +> `mxcli run --local` runs it, `mxcli run --hub` exposes it at a public URL, and +> `mxcli playwright` / `mxcli oql` / `mxcli test --local` exercise it. A container can run the +> whole loop. For the full headless build-and-prove run, use the full-e2e cloud prompt in +> `personal-toolkit/prompts/` instead of this one. --- ``` This is a fresh Claude Code session with no prior context. Task: draft and static-check the next -phase of a Mendix build plan, using mxcli-project-toolkit's iterative-build-loop.md — WITHOUT -Studio Pro, which this session cannot reach. +phase of a Mendix build plan, using mxcli-project-toolkit's iterative-build-loop.md. Scope is +deliberately static: MDL drafted and applied, model-side checks run, no runtime stood up. Setup (do this first, in order): 1. Add and clone these repos: @@ -27,9 +32,9 @@ Setup (do this first, in order): - https://github.com/mendixlabs/mxcli.git (the mxcli CLI — build/install it) - (contains the .mpr this session will read/write against) -2. From the toolkit clone, run `bin/doctor.sh `. It will (correctly) warn that - Studio Pro automation is unavailable here — that's expected, not a problem to fix. Everything - else it reports missing, fix before continuing. +2. From the toolkit clone, run `bin/doctor.sh `. A warning that Studio Pro + automation is unavailable is expected here and is not a problem to fix — nothing in this + prompt's scope uses it. Everything else it reports missing, fix before continuing. 3. Confirm no live Studio Pro elsewhere holds a lock on this project's .mpr (check for a `*.mpr.lock` file, or ask me). mxcli must never touch a .mpr while Studio Pro has it open, including reads — if uncertain, stop and ask rather than risk corrupting the model. @@ -51,20 +56,19 @@ Scope — run through "The Build Loop" steps 1-9 ONLY, then stop: - project-bin/graph-sweep.sh --module - project-bin/coverage-check.sh, against the module's coverage ledger -DO NOT attempt, and do not report as done — hand these back to me explicitly instead: -- Gate: BUILD (bin/exec.sh's mxbuild run + snapshot/auto-restore) — exec.sh's SP handling - requires a live, reachable Studio Pro. -- Reopening Studio Pro, "Update security", or any Cmd+S save. +OUT OF SCOPE for this prompt — do not attempt, and hand these back to me explicitly instead. +These are excluded because this run is deliberately static, NOT because they are impossible here: +- Gate: BUILD (project-bin/exec.sh's mxbuild run + snapshot/auto-restore). Runs fine headless; + it is out of scope only because nothing here stands up a runtime to prove the result. - Gate: UI (module-review.md's PROVE/LOOK stages, project-bin/verify-module.sh, the happy-path - walk) — these need a running app + reachable DB, same as the e2e test prompt - (commands/mobile-auto-test-prompt.md) requires, and this session has neither by default. + walk) — these need a running app + reachable DB, which this prompt does not set up. - Renaming any script to its `done-` prefix — that rename only happens after the FULL gate - (mxbuild + SP reopen + happy-path) passes, which this session cannot verify. + (mxbuild + happy-path) passes, which this session does not verify. Deliverable: the drafted/applied MDL for this phase, syntax-clean per step 6, plus the results of the four static instruments in step 7, plus a short handoff note listing exactly what's left -(mxbuild gate, SP reopen, happy-path walk, module-review.md, done- rename) for me to run locally -or in a session that can reach Studio Pro / the running app. +(mxbuild gate, happy-path walk, module-review.md, done- rename) for me to run in a session that +stands up the app. If you hit anything requiring a judgement call outside this scope (an ambiguous CE error, a requirements gap, whether to touch a shared module), stop and ask rather than guessing — same diff --git a/skills/checkpoints/checkpoint-architecture.md b/skills/checkpoints/checkpoint-architecture.md index 43c08cd..da7d9c2 100644 --- a/skills/checkpoints/checkpoint-architecture.md +++ b/skills/checkpoints/checkpoint-architecture.md @@ -155,7 +155,7 @@ the artifact check reports it PENDING until it exists. > 'must be certified for [standard]'). Drop them here or say 'none for POC.'" **What to do with the answer:** -- Add to `project-profile.md` under `## Non-Functional Requirements` +- Record them in `PROJECT.md` under `## Decisions` as `NFRs:` — one line each, `CONFIRMED` - Flag any that directly affect module structure (e.g. multi-tenancy → affects all XPath constraints) - Note deferred NFRs in `PROJECT.md` so they aren't lost diff --git a/skills/checkpoints/checkpoint-design.md b/skills/checkpoints/checkpoint-design.md index 241b96a..f403b4f 100644 --- a/skills/checkpoints/checkpoint-design.md +++ b/skills/checkpoints/checkpoint-design.md @@ -82,7 +82,7 @@ or desktop-first. Use that to set the recommended option. | Answer type | Action | |---|---| -| Figma link | Add to `project-profile.md` → External References. Note which flows are designed vs wireframe-only. | +| Figma link | Add to `PROJECT.md` → `## Decisions` → `Design assets:`. Note which flows are designed vs wireframe-only. | | Brand doc / PDF | Same as above. Extract: primary color, font family, logo usage rules. | | Verbal description | Record key decisions (primary color, font, tone) in `design-artifacts.md` inputs. | | "Atlas defaults" | Note it. No custom tokens needed. Skip Atlas customization in MDL layer. | @@ -98,8 +98,3 @@ PROJECT.md → ## Decisions: Platform target: [web / responsive / native / both] Design assets: [Figma URL / brand doc link / 'Atlas defaults' / description] ``` - -``` -project-profile.md → ## External References: - Design: [link or 'Atlas defaults'] -``` diff --git a/skills/checkpoints/checkpoint-scope.md b/skills/checkpoints/checkpoint-scope.md index 06ac14a..c230a61 100644 --- a/skills/checkpoints/checkpoint-scope.md +++ b/skills/checkpoints/checkpoint-scope.md @@ -97,7 +97,7 @@ If found: option A = reuse it. If not found: offer build-new vs manual-only. > the target? Drop a link or paste the key constraints — or say 'none' if this is code-only." **What to do with the answer:** -- If a link is provided: add it to `project-profile.md` under `## External References` +- If a link is provided: add it to `PROJECT.md` under `## Decisions` as `External refs:` - If constraints are pasted: record them in `PROJECT.md` under `## Decisions` - If 'none': note it and proceed diff --git a/skills/e2e-harness-base.md b/skills/e2e-harness-base.md index a501eff..946aed9 100644 --- a/skills/e2e-harness-base.md +++ b/skills/e2e-harness-base.md @@ -18,7 +18,8 @@ Build after completing a module build phase: set of idempotent seed SQL scripts run directly against Postgres. See "Seed-data bootstrap" below — the DB smoke check is now expected to self-heal a missing/fresh case rather than just fault the harness on it. -- App running locally (`mxcli docker run -p App.mpr --wait`) +- App running locally (`mxcli docker run -p App.mpr --wait`, or `mxcli run --local` for a + Docker-free warm loop — see `testing-shape.md` for the flag table) --- @@ -27,7 +28,9 @@ Build after completing a module build phase: - Node.js available - Playwright installed: `npm init -y && npm i -D playwright` - `npx playwright install chromium` -- App running at `http://localhost:8080` +- App running at `http://localhost:8080` — or, when this session cannot reach that host (a + cloud container, a devcontainer, a phone), at the public URL from `mxcli run --local --hub`. + Point the harness `BASE_URL` at whichever one actually answers; never assume `localhost:8080`. - A working data-assertion instrument. **Prefer the M2EE admin API** (`mxcli oql --direct`, `adminPort = runtime port + 10`, token from the project's own m2ee config) — see `learned-db-assertions.md`. The `psql.exe` config further down is the Windows-only diff --git a/skills/existing-app-assurance.md b/skills/existing-app-assurance.md index 609b8ac..05e70d6 100644 --- a/skills/existing-app-assurance.md +++ b/skills/existing-app-assurance.md @@ -13,7 +13,14 @@ - Before a Mendix version upgrade or a big refactor: record what the app does *today* so you can prove it still does it afterwards. - You inherited an app and need to understand it before touching it. -Not for building anything new — that's `conversion-runbook.md` (pick an entry mode there). +Not for building anything new. Two different places to go, depending on what "new" means: + +- **Changing the app you just audited** — adding a feature, altering a flow, restructuring a module → + `existing-app-change.md`. That skill starts where this one ends: Track B's baseline is its + precondition, not an optional extra, because an app with no specification has nothing else to be + checked against. +- **Building a different app** — a migration from a non-Mendix source, or a new app → + `conversion-runbook.md` (pick an entry mode there). ## Ground rules diff --git a/skills/existing-app-change.md b/skills/existing-app-change.md new file mode 100644 index 0000000..3960a81 --- /dev/null +++ b/skills/existing-app-change.md @@ -0,0 +1,179 @@ +# Change an Existing App — Adding To or Altering a Mendix App You Already Have + +**Applies to:** any mxcli project — a live Mendix app, built by people, with no BRDs, no architecture +document and no wireframes, that you now need to change. +**Requires:** the app's `.mpr`, a runnable local environment, bash and Python 3. Run `bin/doctor.sh` +once on a new machine. Windows: Git Bash, and see `conversion-runbook.md` → Prerequisites. +**Purpose:** The recipe for changing an app the pipeline never built. The knowledge base comes from +**the model itself**, not from an extractor, and stages 2–4 run over the **slice you are changing** — +not over the whole application. +**Source:** Field run #1, 2026-08-20. The three entry modes all assume the target app does not exist +yet, and the classification rules sent an existing `.mpr` to Migration — i.e. to migrating the app to +itself. `existing-app-assurance.md` covers audit and testing and says in as many words *"not for +building anything new"*, which sent you back to the three modes that did not fit. This file closes +that loop. + +--- + +## When to Use This Skill + +- "Add click-and-collect to our webshop." +- "The approval flow needs a second approver above €250." +- "Replace the CSV export with a real ERP integration." +- "This module is a mess and we want to restructure it" — restructuring is a change like any other. + +**Not this skill:** + +| You want | Go to | +|---|---| +| To understand, audit or lint the app; a regression net; no change planned | `existing-app-assurance.md` | +| To rebuild the app on Mendix from a non-Mendix source | `conversion-runbook.md` → Migration | +| A brand-new app | `conversion-runbook.md` → Requirements-driven or Greenfield | +| A one-line fix you would not open a ticket for | Just do it. This skill is overhead for a typo. | + +The line between "a change" and "a new app inside an old one" is scope, and Stage 0 draws it. If the +slice turns out to be most of the app, you are in Migration mode with a Mendix source — say so and +switch. + +--- + +## Ground rules + +**1. The regression net comes first, and it is not optional.** + +Every other entry mode has a specification to check the result against. This one does not: the app is +its own specification and nobody wrote it down. The substitute is a proven statement of what the app +does *today*, before you touch it — `existing-app-assurance.md` Track B, at minimum over the modules +your change touches. + +Skip it and you have no way to tell a bug you introduced from behaviour that was always like that. +That is the entire risk of this entry mode, concentrated in one decision. + +**2. The slice is the unit of work, not the app.** + +You are not writing BRDs for a five-year-old application. You are writing them for what changes, plus +what that change touches. Everything downstream — the ledger denominator, the coverage claim, the +gates — is scoped to the slice. A coverage number that silently means "of the slice" while reading +like "of the app" is the false-green this toolkit keeps producing; say which one you mean, every time. + +**3. The model is ground truth. Memory and the UI are not.** + +Ask the model, not the app's screens and not the person who thinks they remember. `query-the-model.md` +is baseline routing for a reason, and here it is the primary instrument rather than a convenience. + +**4. Record the as-is before you record the to-be.** + +For each thing you change, write what it does now and what it should do. The as-is half is the piece +that gets skipped and the piece that has no other home — after the change it is unrecoverable except +from git history of a `.mpr`, which is not a readable record. + +--- + +## Where the knowledge base comes from — Path D + +`conversion-runbook.md` Stage 1 names three paths: **A** code → AST extractors, **B** documents → LLM +extraction, **C** SME interview. This entry mode adds a fourth. + +**Path D — the live model.** No extractor, no parsing, no regex. `mxcli` reads the `.mpr` and answers +directly: + +```bash +./mxcli -p .mpr -c "SHOW MODULES" +./mxcli -p .mpr -c "SHOW ENTITIES IN " +./mxcli -p .mpr -c "SHOW ASSOCIATIONS" +./mxcli -p .mpr -c "DESCRIBE ENTITY ." +./mxcli -p .mpr -c "SHOW PAGES IN " +./mxcli -p .mpr -c "SHOW MICROFLOWS IN " +mxcli graph-report # dependency tangles, community detection +``` + +**Path D does not have Path A's failure mode, and that is the main reason this entry mode is safer +than it looks.** A code extractor guesses at files and silently returns zero for a construct class +whose layout it did not anticipate — field run #1 lost 39 Angular components, 35 REST endpoints and +18 SQL tables that way, with every instrument reporting green. The model cannot do this. It either +answers or errors, and `SHOW ENTITIES` is the count, not an estimate of the count. + +Still record the counts. `SHOW MODULES` returning 6 when the person who asked for the change said +"about twenty" is a scope conversation you want to have at Stage 0, not at Stage 4. + +--- + +## Blast radius — the question Stage 0 exists to answer here + +In every other mode, Stage 0 asks *what do we build first*. Here it asks a second question that has no +equivalent elsewhere: **what does this change touch that nobody mentioned?** + +Work outward from the entities the change affects: + +1. **Associations** — `SHOW ASSOCIATIONS`, then `DESCRIBE ENTITY` each end. An entity you are changing + that is on the far end of an association from a module nobody mentioned is your blast radius. +2. **Microflows that read or write those entities** — `SEARCH` for the entity name across microflows. + A change to a validation rule lands in every flow that writes the entity, not only the one on the + screen you were shown. +3. **Pages bound to them** — `SHOW PAGES IN `, and check which are bound to the entity. +4. **Module dependencies** — `mxcli graph-report`. If the module you are changing is in a tangle, the + blast radius is the tangle. +5. **Published and consumed services** — a changed entity behind a published REST service is a + contract change, and the consumer is not in the `.mpr`. + +**Write the blast radius into `triage.md` as its own section and get it confirmed.** It is the single +most valuable output of Stage 0 in this mode, and it is the thing the person requesting the change is +least able to give you — they know what they want, not what it touches. + +--- + +## What runs, stage by stage + +| Stage | Runs? | What changes in this mode | +|---|---|---| +| **P — Kickoff** | Yes, light | `bin/init-project.sh` as normal. Many intake questions are already answered by the app existing — answer them from the model, not by asking. Record entry mode `Change an existing app` `CONFIRMED` in `PROJECT.md`. | +| **0 — Triage & Scope ✋** | **Yes, always** | Two questions, not one: which slice, and its blast radius (above). The Coverage Matrix's *extraction* rows are N/A — Path D has no extractor to choose. The Business Capability Map is built from `SHOW MODULES` + the change request. **CAC-1 runs.** | +| **1 — Analysis** | Yes, **Path D** | Query the model into the knowledge base, scoped to the slice **plus** its blast radius. Path A is declared not-applicable with attribution, not "skipped". Path C (SME) matters more here than anywhere: the model tells you what the app does and nobody wrote down why. **CAC-1b runs** — its scope-out diff is the slice-vs-app statement. | +| **2 — Requirements** | Yes, **slice only** | One BRD per capability *being changed*, each carrying **as-is** and **to-be**. Do not BRD untouched capabilities; record explicitly that you did not, and why. **CAC-2 and CAC-3 run.** | +| **3 — Architecture & Design ✋** | **Conditionally** | Run it in full if the change crosses module boundaries, adds an integration, or alters the domain model. Otherwise it collapses to: which existing module owns this, and does that still hold. **Never invent new module boundaries for an app that already has them** — `modularize-domain.md` is being used to *check* a boundary here, not to draw one. Wireframes only for screens that change; the design system is the app's existing styling, captured, not designed. **CAC-4 runs, scoped to what changes.** | +| **4 — Build Plan ✋** | Yes, **slice only** | `brd-to-build-plan.md` as normal, over the slice's BRDs. Ordering must respect what already exists: you cannot stub an entity that is live and has data in it. **CAC-5 runs.** | +| **5–6 — Build & Verify** | Yes, unchanged | `iterative-build-loop.md`, the STOP table, module briefs, gates. Plus the rule below. | +| **7 — Cutover** | **N/A** | There is no legacy system to cut over from. Mark it N/A in the register with that reason — an unstated skip and a settled one are different things, which is the whole reason the register exists. | + +--- + +## The rule that is specific to this entry mode + +**Every gate from Stage 5 onward is also a regression gate.** + +In the other modes a gate asks *did we build what we said*. Here it asks that, and one more thing: +*does everything the app did yesterday still work*. That second question is answered by the Track B +baseline from Ground Rule 1, re-run, and by nothing else. + +A module gate that is green on the new work and was never run against the baseline has answered half +the question and reported it as the whole. Re-run the baseline at every module gate, not once at the +end — a regression found three modules later costs the same to fix and much more to find. + +--- + +## Coverage in this mode + +`coverage-ledger.md`'s denominator is **the slice's BRD leaves**, never the app's. This is the one +number most likely to be read as more than it is. + +State it in the ledger header, in these terms: *"N leaves across M BRDs, covering the click-and-collect +change and its blast radius. The remaining 14 modules of this app have no BRD and are not claimed by +this ledger."* A ledger that says `100% covered` without that sentence is telling the reader the app is +fully specified, and it is not. + +`existing-app-assurance.md` documents four fallback levels for a missing ledger and calls **Level 4 · +NOT APPLICABLE** the normal state for an audited app. In this mode the changed modules move up to a +real ledger and the untouched ones stay at Level 4. Both verdicts in one project is correct here, and +it is the only entry mode where that is true. + +--- + +## Deliverables + +- `PROJECT.md` — decision register, entry mode `CONFIRMED`, blast radius confirmed, Stage 7 marked N/A with its reason. +- `triage.md` — slice + blast radius, signed off. +- Knowledge base — Path D, scoped, with counts recorded: modules, and per in-scope module the entity, page and microflow totals `SHOW …` returned, so a reader can see the slice against the app. +- BRDs for the changed slice, each with as-is and to-be — and one line saying how many capabilities were *not* BRD'd and why. +- A build plan over the slice. +- **A regression baseline that was green before the change and is green after it** — stated as N journeys over M modules, re-run at every module gate. Without this the rest is unverified. +- Anything reusable learned about the app → `skills/learned-*.md`; process learnings → `process/process-learnings.md`. diff --git a/skills/learned-mdl-preflight.md b/skills/learned-mdl-preflight.md index 7692418..0e5a3f2 100644 --- a/skills/learned-mdl-preflight.md +++ b/skills/learned-mdl-preflight.md @@ -70,6 +70,7 @@ Once you've picked a mode per operation, run the STOP table below against every --- | 23 | Name any document or entity that **might already exist** — `create or modify` on an entity, or any `create or replace` on a page, layout or microflow | **STOP → run `mxcli diff -p .mpr