diff --git a/cmd/mxcli/syntax/capability_docs_drift_test.go b/cmd/mxcli/syntax/capability_docs_drift_test.go index 2c78ffe9d..c1950bd90 100644 --- a/cmd/mxcli/syntax/capability_docs_drift_test.go +++ b/cmd/mxcli/syntax/capability_docs_drift_test.go @@ -50,6 +50,29 @@ func readDoc(t *testing.T, name string) string { // `mxcli syntax` topic is NOT sitting in the docs' "no MDL surface at all" // table. The syntax registry is populated from the code, so it cannot claim a // topic for something that does not exist. +// shippedCapabilities pairs a capability with the syntax topic that PROVES it +// ships. A topic is registered from Go code, so the pairing cannot go stale in +// the direction that matters: delete the feature and the topic goes with it. +// +// The last three were added after an audit found the matrix claiming all three +// were unavailable: "Microflow rules" and "Message definitions" sat under "Not +// Yet Implemented" (i.e. "no MDL surface at all") while both were authorable, +// and Layouts were described as "Read-only, no syntax topic" in three separate +// gap lists. None of it was caught, because this table did not name them — a +// hand-maintained guard only guards what someone remembered to add. +var shippedCapabilities = []struct{ topic, claim string }{ + {"database-connection", "Ext. DB connector"}, + {"queue", "Task queue"}, + {"scheduled-event", "Scheduled events"}, + {"regular-expression", "Regular expressions"}, + {"image-collection", "Image collection"}, + {"navigation.menu-document", "Menus"}, + {"workflow", "Workflows"}, + {"layout", "Layouts"}, + {"microflow.rule", "Microflow rules"}, + {"message-definition", "Message definitions"}, +} + func TestCapabilityDocsDoNotClaimShippedFeaturesAreMissing(t *testing.T) { matrix := readDoc(t, "MDL_FEATURE_MATRIX.md") @@ -66,15 +89,7 @@ func TestCapabilityDocsDoNotClaimShippedFeaturesAreMissing(t *testing.T) { // Each capability that must not appear as unimplemented, keyed by the syntax // topic that proves it ships. A topic is registered from Go code, so this // pairing cannot go stale in the direction that matters. - for _, c := range []struct{ topic, claim string }{ - {"database-connection", "Ext. DB connector"}, - {"queue", "Task queue"}, - {"scheduled-event", "Scheduled events"}, - {"regular-expression", "Regular expressions"}, - {"image-collection", "Image collection"}, - {"navigation.menu-document", "Menus"}, - {"workflow", "Workflows"}, - } { + for _, c := range shippedCapabilities { if ByPath(c.topic) == nil { t.Errorf("no `mxcli syntax %s` topic — either the feature was removed "+ "(then drop this row) or the topic is missing (then add it)", c.topic) @@ -122,3 +137,36 @@ func TestMissingCapabilitiesIsMarkedAsDated(t *testing.T) { } } } + +// TestMissingSyntaxTopicsAreActuallyMissing closes the hole that let Layouts be +// listed as "Read-only, no syntax topic" while `mxcli syntax layout` answered. +// +// This is a direct contradiction rather than a judgement call, which is why it +// can be asserted mechanically: the section states a topic does not exist, and +// the registry says it does. Deliberately narrower than the Skills/Examples gap +// lists, where an entry can be true at the same time as a syntax topic exists — +// Regular Expressions has a topic AND genuinely has no skill. +func TestMissingSyntaxTopicsAreActuallyMissing(t *testing.T) { + matrix := readDoc(t, "MDL_FEATURE_MATRIX.md") + + start := strings.Index(matrix, "### Missing Syntax Topics") + if start < 0 { + t.Fatal(`MDL_FEATURE_MATRIX.md has no "### Missing Syntax Topics" section — ` + + `if it was renamed, update this guard rather than deleting it`) + } + section := matrix[start:] + if end := strings.Index(section, "\n### "); end > 0 { + section = section[:end] + } + + for _, c := range shippedCapabilities { + if ByPath(c.topic) == nil { + continue // covered by the sibling test, which reports it there + } + if strings.Contains(section, c.claim) { + t.Errorf("MDL_FEATURE_MATRIX.md lists %q under \"Missing Syntax Topics\", "+ + "but `mxcli syntax %s` resolves — the doc tells a reader to look for "+ + "syntax that is already published", c.claim, c.topic) + } + } +} diff --git a/docs-site/src/appendixes/quick-reference.md b/docs-site/src/appendixes/quick-reference.md index 24e4914f8..a11276dad 100644 --- a/docs-site/src/appendixes/quick-reference.md +++ b/docs-site/src/appendixes/quick-reference.md @@ -541,7 +541,7 @@ Cross-reference commands require `REFRESH CATALOG FULL` to populate reference da | Stdin piping | `echo "CMD" \| mxcli -p app.mpr` | Quiet mode, pipe-friendly | | Check syntax | `mxcli check script.mdl` | Parse-only validation | | Check references | `mxcli check script.mdl -p app.mpr --references` | With reference validation | -| Lint project | `mxcli lint -p app.mpr [--format json\|sarif]` | 14 built-in + 27 Starlark rules | +| Lint project | `mxcli lint -p app.mpr [--format json\|sarif]` | 19 built-in + 31 Starlark rules | | Report | `mxcli report -p app.mpr [--format markdown\|json\|html]` | Best practices report | | Test | `mxcli test tests/ -p app.mpr` | `.test.mdl` / `.test.md` files | | Diff script | `mxcli diff -p app.mpr changes.mdl` | Compare script vs project | diff --git a/docs-site/src/migration/validation.md b/docs-site/src/migration/validation.md index 2df058ccf..0c173d2ac 100644 --- a/docs-site/src/migration/validation.md +++ b/docs-site/src/migration/validation.md @@ -11,7 +11,7 @@ mxcli check script.mdl # 2. Reference validation (checks entity/microflow names exist) mxcli check script.mdl -p app.mpr --references -# 3. Lint the full project (41 built-in + 27 Starlark rules) +# 3. Lint the full project (19 built-in + 31 Starlark rules) mxcli lint -p app.mpr # 4. Quality report (scored 0-100 per category) diff --git a/docs-site/src/reference/capabilities.md b/docs-site/src/reference/capabilities.md index 5bf56c230..f8a8bef35 100644 --- a/docs-site/src/reference/capabilities.md +++ b/docs-site/src/reference/capabilities.md @@ -110,7 +110,7 @@ Everything mxcli can do, organized by use case. | Cross-references | `LIST CALLERS/CALLEES OF` | Who calls what | | Impact analysis | `LIST IMPACT OF Module.Entity` | What breaks if I change this | | Transitive callers | `LIST CALLERS OF ... TRANSITIVE` | Full call chain | -| Linting | `mxcli lint -p app.mpr` | 14 built-in + 27 Starlark rules | +| Linting | `mxcli lint -p app.mpr` | 19 built-in + 31 Starlark rules | | Best practices report | `mxcli report -p app.mpr` | Scored report with categories | | Missing translations | QUAL005 linter rule | Detects incomplete translations | | Catalog queries | `SELECT ... FROM CATALOG.tables` | SQL over project metadata | diff --git a/docs-site/src/tutorial/validation.md b/docs-site/src/tutorial/validation.md index 3003709d2..c668447f6 100644 --- a/docs-site/src/tutorial/validation.md +++ b/docs-site/src/tutorial/validation.md @@ -184,7 +184,7 @@ For a broader set of checks across the entire project (not just a single script) mxcli lint -p app.mpr ``` -This runs 14 built-in rules plus 29 Starlark rules covering security, architecture, quality, and naming conventions. See `mxcli lint --list-rules` for the full list. +This runs 19 built-in rules plus 31 Starlark rules covering security, architecture, quality, and naming conventions. See `mxcli lint --list-rules` for the full list. For CI/CD integration, output in SARIF format: diff --git a/docs/01-project/MDL_FEATURE_MATRIX.md b/docs/01-project/MDL_FEATURE_MATRIX.md index e1e3bc5f3..9d2c96df6 100644 --- a/docs/01-project/MDL_FEATURE_MATRIX.md +++ b/docs/01-project/MDL_FEATURE_MATRIX.md @@ -166,10 +166,11 @@ live distinction is **MPR vs MCP**. | **Associations** | Y | Y | Y | N | Y | Y | 01 | Y | N | Y | Y | Y | Y | Y | Y | Y | N | | **Enumerations** | Y | Y | Y | Y | Y | Y | 01 | Y | Y | N | Y | Y | Y | N | Y | Y | Y | | **Microflows** | Y | Y | Y | Y | Y | N | 02 | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | -| **Nanoflows** | Y | Y | Y | Y | Y | N | Y | Y | Y | Y | Y | Y | Y | Y | P | N | N | +| **Nanoflows** | Y | Y | Y | Y | Y | N | 02b | Y | Y | Y | Y | Y | Y | Y | P | N | N | +| **Rules** | Y | Y | Y | Y | Y | N | Y | Y | Y | Y | N | Y | Y | N | N | Y | N | | **Pages** | Y | Y | Y | N | Y | Y | 03 | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | | **Snippets** | Y | Y | Y | N | Y | Y | 03 | Y | Y | Y | Y | Y | Y | N | Y | Y | Y | -| **Layouts** | Y | Y | N | N | N | N | N | N | Y | Y | Y | N | Y | N | Y | N | N | +| **Layouts** | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | N | Y | Y | N | | **Java Actions** | Y | Y | Y | N | Y | N | 07 | Y | Y | Y | Y | Y | Y | N | Y | Y | N | | **Constants** | Y | Y | Y | Y | Y | N | 09 | Y | N | P | Y | N | Y | N | P | N | N | | **OData Clients** | Y | Y | Y | Y | Y | Y | 10 | Y | Y | P | Y | Y | Y | N | Y | Y | N | @@ -178,24 +179,25 @@ live distinction is **MPR vs MCP**. | **Modules** | Y | Y | Y | N | Y | N | all | Y | Y | Y | Y | Y | Y | N | Y | N | N | | **Navigation** | Y | Y | Y | - | - | Y | 11 | N | Y | Y | Y | Y | Y | N | N | Y | N | | **Business Events** | Y | Y | Y | N | Y | N | 13 | N | Y | N | Y | N | Y | N | Y | Y | N | -| **Project Settings** | Y | Y | - | - | - | Y | N | N | Y | Y | Y | N | Y | N | N | Y | P | -| **Task Queues** | Y | Y | Y | Y | Y | N | 21 | Y | Y | N | N | Y | Y | N | Y | Y | N | -| **Scheduled Events** | Y | Y | Y | Y | Y | N | 21 | Y | Y | Y | N | Y | Y | N | Y | Y | N | +| **Project Settings** | Y | Y | - | - | - | Y | 14 | N | Y | Y | Y | N | Y | N | N | Y | P | +| **Task Queues** | Y | Y | Y | Y | Y | N | Y | Y | Y | N | N | Y | Y | N | Y | Y | N | +| **Scheduled Events** | Y | Y | Y | Y | Y | N | Y | Y | Y | Y | N | Y | Y | N | Y | Y | N | | **Database Connections** | Y | Y | Y | Y | Y | N | 05 | Y | Y | N | N | Y | Y | N | Y | Y | N | -| **Regular Expressions** | Y | Y | Y | Y | Y | N | N | Y | Y | Y | N | N | Y | N | Y | Y | N | -| **Validation Rules** | - | Y | Y | - | - | Y | N | Y | N | Y | N | N | Y | N | N | Y | N | -| **Menus** | - | Y | Y | Y | Y | N | N | Y | N | N | N | N | Y | N | N | Y | N | -| **Image Collections** | Y | Y | Y | N | Y | N | N | Y | N | N | N | N | Y | N | Y | Y | N | -| **JavaScript Actions** | Y | Y | Y | N | Y | N | N | Y | Y | Y | N | N | Y | N | Y | N | N | -| **Published REST Services** | Y | Y | Y | Y | Y | N | N | N | Y | N | P | N | Y | N | N | Y | N | +| **Regular Expressions** | Y | Y | Y | Y | Y | N | Y | Y | Y | Y | N | N | Y | N | Y | Y | N | +| **Validation Rules** | - | Y | Y | - | - | Y | Y | Y | N | Y | N | N | Y | N | N | Y | N | +| **Menus** | - | Y | Y | Y | Y | N | 26 | Y | N | N | N | N | Y | N | N | Y | N | +| **Image Collections** | Y | Y | Y | N | Y | N | 19 | Y | N | N | N | N | Y | N | Y | Y | N | +| **JavaScript Actions** | Y | Y | Y | N | Y | N | 07b | Y | Y | Y | N | N | Y | N | Y | N | N | +| **Published REST Services** | Y | Y | Y | Y | Y | N | 22 | N | Y | N | P | N | Y | N | N | Y | N | | **REST Clients** | Y | Y | Y | Y | Y | Y | 06 | Y | Y | P | Y | Y | Y | N | Y | Y | N | -| **Import Mappings** | Y | Y | Y | N | Y | N | 06 | Y | N | N | P | Y | N | N | N | Y | N | -| **Export Mappings** | Y | Y | Y | N | Y | N | 06 | Y | N | N | P | Y | N | N | N | Y | N | +| **Import Mappings** | Y | Y | Y | N | Y | N | 21 | Y | N | N | P | Y | N | N | N | Y | N | +| **Export Mappings** | Y | Y | Y | N | Y | N | 21 | Y | N | N | P | Y | N | N | N | Y | N | | **JSON Structures** | Y | Y | Y | Y | Y | N | 20 | Y | N | N | P | N | N | N | N | N | N | -| **Workflows** | Y | Y | Y | N | Y | Y | N | Y | Y | Y | N | Y | Y | N | N | Y | N | -| **AI Agent documents** | Y | Y | Y | N | Y | N | N | Y | N | N | N | Y | Y | N | Y | Y | N | -| **Pluggable widgets** | Y | Y | Y | - | Y | Y | 03 | Y | N | N | P | Y | Y | N | N | Y | N | -| **Data Transformers** | Y | Y | Y | N | Y | N | N | Y | N | N | N | N | Y | N | Y | Y | N | +| **Message Definitions** | Y | Y | Y | Y | Y | Y | 40 | Y | N | N | N | N | Y | N | N | Y | N | +| **Workflows** | Y | Y | Y | N | Y | Y | 24 | Y | Y | Y | N | Y | Y | N | N | Y | N | +| **AI Agent documents** | Y | Y | Y | N | Y | N | 27 | Y | N | N | N | Y | Y | N | Y | Y | N | +| **Pluggable widgets** | Y | Y | Y | - | Y | Y | 30 | Y | N | N | P | Y | Y | N | N | Y | N | +| **Data Transformers** | Y | Y | Y | N | Y | N | 23 | Y | N | N | N | N | Y | N | Y | Y | N | ## Security Features @@ -214,7 +216,7 @@ live distinction is **MPR vs MCP**. | Feature | SHOW | DESCRIBE | CREATE | OR MODIFY | DROP | ALTER | Examples | Tests | Catalog | REFS | LSP | Skills | Help | Viz | REPL | Syntax | Starlark | |---------|------|----------|--------|-----------|------|-------|----------|-------|---------|------|-----|--------|------|-----|------|--------|----------| -| **Folders** | N | N | P | N | N | N | N | P | N | N | P | Y | Y | - | N | N | N | +| **Folders** | N | N | P | N | N | N | 18 | P | N | N | P | Y | Y | - | N | N | N | | **MOVE** | - | - | - | - | - | - | N | P | N | N | P | Y | Y | - | N | Y | N | ## External SQL & Data @@ -236,7 +238,7 @@ live distinction is **MPR vs MCP**. | **Catalog Query** | `select ... from CATALOG.` | Y | Y | SQL against project metadata | | **Cross-References** | `show callers/callees/references/impact/context of` | Y | Y | Requires `refresh catalog full` | | **Full-Text Search** | `search ''` | Y | Y | Across all strings and source | -| **Linting** | `mxcli lint -p app.mpr` | Y | Y | 14 built-in + 27 Starlark rules | +| **Linting** | `mxcli lint -p app.mpr` | Y | Y | 19 built-in + 31 Starlark rules | | **Report** | `mxcli report -p app.mpr` | Y | Y | Scored best practices report | | **Widget Discovery** | `show widgets [in module] [where ...]` | Y | Y | Experimental | | **Widget Update** | `update widgets set ... where ...` | Y | Y | Bulk pluggable widget updates | @@ -294,7 +296,6 @@ These types are not covered in `help.go` output: ### Missing Skills -- **Layouts** — Read-only, no skill needed - **Constants** — No dedicated skill ### Missing Tests @@ -303,7 +304,6 @@ These types are not covered in `help.go` output: ### Missing Examples -- **Layouts** — Read-only, no example needed - **Folders / MOVE** — No dedicated example file ### Missing REPL Autocomplete @@ -318,7 +318,6 @@ These types are not covered in `help.go` output: - **Constants** — No `mxcli syntax constant` topic - **Nanoflows** — No dedicated syntax topic (covered by microflow topic) -- **Layouts** — Read-only, no syntax topic - **Modules** — No dedicated syntax topic ### Missing Starlark APIs @@ -368,8 +367,6 @@ Document types that exist in Mendix and have **no** MDL surface at all. | Feature | Notes | |---------|-------| -| **Microflow rules** (`Microflows$Rule`) | Reusable decision logic called from a microflow. Not to be confused with `CREATE VALIDATION RULE`, which is an attribute constraint and *is* supported | -| **Message definitions** (`MessageDefinitions$MessageDefinitionCollection`) | Message definition documents | | **XML schemas** | Imported XSD documents | | **Web service publish / consume** | SOAP. `CALL WEB SERVICE` exists in microflows for a stored service; the service documents themselves are not authorable | | **Data importer** | Excel/CSV import documents | diff --git a/docs/01-project/MDL_QUICK_REFERENCE.md b/docs/01-project/MDL_QUICK_REFERENCE.md index 67ca255ca..b9606b4dc 100644 --- a/docs/01-project/MDL_QUICK_REFERENCE.md +++ b/docs/01-project/MDL_QUICK_REFERENCE.md @@ -1867,7 +1867,7 @@ Name the widget the way you write it in a page body. The target is stored as the | Execute script | `mxcli exec script.mdl -p app.mpr` | Script file | | Check syntax | `mxcli check script.mdl` | Parse-only validation | | Check references | `mxcli check script.mdl -p app.mpr --references` | With reference validation | -| Lint project | `mxcli lint -p app.mpr [--format json\|sarif]` | 15 built-in + 27 Starlark rules | +| Lint project | `mxcli lint -p app.mpr [--format json\|sarif]` | 19 built-in + 31 Starlark rules | | Report | `mxcli report -p app.mpr [--format markdown\|json\|html]` | Best practices report | | Test | `mxcli test tests/ -p app.mpr` | `.test.mdl` / `.test.md` files | | Diff script | `mxcli diff -p app.mpr changes.mdl` | Compare script vs project |