Skip to content

Describe round trip: delete close page, $[%…%] returns, navigation-list items (#950) - #959

Merged
ako merged 22 commits into
mainfrom
fix/950-describe-roundtrip
Oct 3, 2026
Merged

ako merged 22 commits into
mainfrom
fix/950-describe-roundtrip

Conversation

@ako

@ako ako commented Oct 3, 2026

Copy link
Copy Markdown
Owner

Closes #950

All three items are fixed. Each is proven by describe → check → exec → describe, with the stored value compared and not only the re-described text.

1. delete close page lost ClosePage on write (silent)

  • buildClientActionV3Base's delete case now copies action.ClosePage. The visitor sets it and the writer writes it, but the builder dropped it.
  • Audit of every page-action case:
    • save and cancel already copied ClosePage.
    • The with (…) settings (confirmation, progress, DisabledDuringExecution) go through applyActionSettings, which is generic.
    • No other flag that the visitor sets is dropped.
  • Describe prints every ClosePage it reads.
  • Two values are hard-coded and cannot be written in MDL. Both are listed under follow-ups.

2. return $[%CurrentUser%];

  • Describe used to add $ to anything that had none of the characters +'"(). That also turned return if … then … else … into return $if …, which hit two TestApp WorkflowCommons microflows.
  • It now adds $ only to a bare name with an optional /path, which is the shape a legacy writer stored without the $.
  • The other places in describe that add $ do so to variable-name fields, not to expressions. They are safe and unchanged.

3. Navigation-list items

  • Action: extractNavigationListItemAction now uses the shared client-action renderer, so actions print as show page M.P, call microflow …, sign out and so on.
  • Unnamed items: fixing the parse error exposed the next failure. Studio Pro leaves items unnamed, and exec refused that with "item inside navigationlist requires a name". Exec now accepts an unnamed item, and describe prints it as item (…).
  • Writer: for an unnamed item, the writer now leaves out the Name key and writes a null ConditionalVisibilitySettings. This matches all 6 Studio Pro-authored items in TestApp. Before this, a describe → exec of Rules.Entity_Menu still rewrote the snippet.
  • mx check (11.14.0) on a TestApp copy with unnamed items written by exec: 0 errors.

Harness

  • New mdl/roundtrip/describe_reparse_950_test.go (PedApp). For each of the three cases it runs describe → checkReferences → exec → describe, checks that nothing was written, and checks the stored ClosePage per button. A plain delete button is the control.
  • Struck from the TestApp allowlist:
    • snippet Rules.Entity_Menu
    • microflow WorkflowCommons.SUB_Workflow_AverageHandlingTime
    • microflow WorkflowCommons.SUB_WorkflowTask_AverageHandlingTime

Test plan

  • make build
  • go test ./mdl/executor/ ./mdl/backend/modelsdk/ ./mdl/visitor/ ./cmd/mxcli/: ok
  • go test -tags integration ./mdl/roundtrip/ -run 'TestTestAppRoundTrip$|TestPedAppRoundTrip$|DescribeUsesCanonicalSpellings|DescribeIsValid|TestDescribeReExecutes_950|Pluggable': ok
  • make lint (includes check-conformance): ok
  • make check-findings: ok
  • End to end with the CLI on fresh copies:
    • jts App: delete close page → bson dump shows ClosePage true on that button and false on the plain delete button; describe → exec gives "Unchanged".
    • TestApp: describe snippet Rules.Entity_Menu → check --references → exec → describe gives identical output and "Unchanged".
    • mxcli docker check on the result: 0 errors.

Revert checks. Each fix was removed in turn and the tests were run again:

Fix removed What failed
Builder ClosePage copy Harness: as created, bDelClose stores ClosePage=false. Unit test: "describe printed delete close page, which builds ClosePage=false".
Return-value $ rule Harness: describe has no return [%CurrentUser%];. Unit test: $[%CurrentUser%] does not parse (token recognition error at: '$[').
Shared renderer for nav items Harness and unit test: described show_page 'Rules.BusinessRule_Overview'.
Accepting unnamed items Harness: "item inside navigationlist requires a name".
Omitting Name on unnamed items in the writer TestTestAppRoundTrip/snippet_Rules.Entity_Menu reports Items[n]/Name: added.
Null visibility slot Writer unit test: ConditionalVisibilitySettings missing.

Follow-ups (not changed here; each needs a syntax decision)

  • complete task: the builder always writes ClosePage: true and Commit: true, and describe prints neither. A Studio Pro outcome button with either one off would be flipped by a describe → exec. All 4 such buttons in TestApp have both true, so this is latent today.
  • close page / show page / create object: the number of pages to close (NumberOfPagesToClose / NumberOfPagesToClose2) is written as "" and has no MDL form. All stored values in TestApp are "", so this is also latent.

🤖 Generated with Claude Code

ako and others added 22 commits October 3, 2026 14:39
…race (#951)

SaveToFile now writes to a temp file in the cache's directory (VACUUM INTO,
manual-copy fallback into the same temp file) and renames it over the cache.
buildCatalog and refresh catalog communities no longer remove the cache first.
Opening a cache at the current schema version no longer writes to it: a write
on a file renamed underneath an open connection fails with
SQLITE_READONLY_DBMOVED. File-backed connections get a busy_timeout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Starlark rule reading a struct field this mxcli does not expose is now
reported at info level as "rule <ID> needs a newer mxcli (<detail>)"; other
rule failures stay errors. Every rule failure is marked RuleFailure, kept out
of BuildReport's score, summary and categories, and listed in its own
"Rules That Could Not Run" section (ruleFailures in JSON). A configured rule
severity no longer applies to the rule's own failure, and a rule file failing
to load on an undefined name hints at a newer mxcli.

Part of #952.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sync rules and CLAUDE.md (#952)

- init and every sync write .ai-context/mxcli-tooling.json; any -p command
  warns once on stderr when the binary is provably older (releases by number,
  nightlies by tag date, mixed by build date, dev builds never).
- init --sync-skills (alias --sync) refuses from an older binary instead of
  downgrading, and now also refreshes the bundled lint rules (by file name;
  user rules untouched) and the CLAUDE.md/AGENTS.md section between new
  mxcli:begin/end markers, keeping project notes outside them.
- The bootstrap script compares the PATH binary and ./mxcli with the stamp
  before using them, downloads MXCLI_TAG instead of linking an older binary,
  and downloads through a temp file so a symlinked ./mxcli is never written
  through. Binaries <= v0.24.0 cannot read the stamp; the script is their guard.
- init and new create mdlsource/ with a README.

Closes #952.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docker check ran mx update-widgets on the user's project with only the MPRv2
storage snapshotted, so an MPRv1 .mpr was rewritten permanently, and mx check
itself rewrote theme-cache/ and created deployment/sass/ on both formats, with
or without --no-update-widgets. Both mx steps now run on a temporary copy
(build output, caches and VCS folders skipped), mx output is rewritten to the
project's own paths, and the output says that widgets were normalised on a
copy and what that hides (#568, #646). docker build keeps its snapshot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nto itself (#951)

Check copies the project's directory, so a missing .mpr (or a path in /tmp)
would copy an unrelated directory: fail early instead, and give the existing
fake-mx tests a project file of their own. With TMPDIR inside the project the
walk met its own copy and recursed until the path was too long; skip it.
Trim the custom-widgets skill back under the 700-line limit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A control-bar button of a data grid nested inside a data view or list
view passing $currentObject was refused as CE1571, but there the
control bar's $currentObject is the enclosing object and mxbuild 11.13
builds it clean. The walk now carries whether an ancestor data container
supplies an object context; the grid's own data source still never
scopes its control bar, so a top-level grid is still reported.

Part of #953 (item 2).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CE0582 is the React client's error. Measured on mxbuild 11.13.0: a
staticimage and a classic dropdown on a NativePhone_Default page build
clean, the same widgets on an Atlas_Default page are CE0582. A textbox
on an enumeration is CE2421 on both, so MDL-WIDGET39 is unchanged.

Nothing recorded a layout's platform: ListLayouts left Layout.Native
false for every layout, and LayoutType cannot tell the platforms apart.
- backend: Native is the content wrapper's type (NativeLayoutContent)
- catalog: layouts.Platform ("Web" / "Native"), schema 18
- linter: NativePages() joins pages.LayoutRef to layouts without the
  Marketplace filter (the native layouts live in Atlas_Core); MPR012
  skips those pages; Starlark layout.platform
- check: MDL-WIDGET40 skips a page whose layout resolves as native

Part of #953 (item 3).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A microflow or nanoflow whose only content is `return <expr>;` computes
its value in the end event, which ActivityCount does not count. A
non-Void return type is the witness that the end event returns a value,
so those flows are no longer reported.

Part of #953 (item 4).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One finding per entity x role x CREATE/DELETE (111 on a mid-sized app)
becomes one per entity, listing the de-duplicated roles per right. The
test runs both the .claude and the embedded copy of the rule.

Part of #953 (item 5).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`lint` had -m, `report` only --exclude. `report --modules A,B` applies
the same LintContext module filter and scores only the findings located
in a selected module, so project-wide findings do not weigh on a module
score. The selection is printed in markdown, HTML and JSON output.

Part of #953 (item 6).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The visitor set ActionV3.ClosePage for `delete close page` but the page
builder's delete case did not copy it, so a describe -> exec round trip
stored ClosePage=false: the button stopped closing its page, with check,
exec and mx check clean. Audited the other page-action cases: save and
cancel already copy it; no other visitor flag is dropped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The return formatter prefixed $ to any stored return value without one
of + ' " ( ), so [%CurrentUser%] became $[%CurrentUser%] and
`if ... then ... else ...` became `$if ...`, neither of which parses.
Restore the sigil only for the shape a legacy writer stripped it from:
a bare name with an optional attribute path. The other $-adding sites
in describe prefix variable-name fields, not expressions.

Strikes two TestApp WorkflowCommons microflows from the allowlist.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Item actions were printed by a private copy of the page-action renderer
in the legacy `show_page 'M.P'` form, which does not parse; they now
use the shared client-action renderer, as buttons do, for every action
kind. With that fixed, exec refused the description because Studio Pro
leaves items unnamed, and the writer then wrote Name "" and no
visibility slot where Studio Pro stores no Name key and a null
ConditionalVisibilitySettings. An unnamed item is now accepted, described
as `item (...)` and written as Studio Pro writes it (mx check 11.14.0:
0 errors). Strikes TestApp's Rules.Entity_Menu from the allowlist.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
describe -> check -> exec -> describe on PedApp for a delete-close-page
button (stored ClosePage compared, plain delete as control), a flow
returning [%CurrentUser%], and a navigation list with an unnamed item.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…MDL063 covers nanoflows

Two calls to a void action carrying the same output name (Studio Pro names a
JavaScript action's after the action, $RefreshEntity) are accepted by mxbuild
11.13.0, but check reported MDL063 (microflows) or 'already declared in this
scope' (nanoflows), and describe called the model invalid. Measured: a void
call's output name is inert - a later declare of the name builds clean, a use
is CE0109. The return type is resolved from the script or, with -p, the
project; an unresolvable action still counts.

describe keeps printing the stored `$X =`: Studio Pro stores the name with
UseReturnVariable=true, and the bare form would write an empty name.

MDL063 now runs for nanoflows too (flow-wide, as measured in mxbuild): a
duplicate output across if/else branches passed check and was CE0111. The
check-time body validator no longer reports duplicate names for flows - it
scoped them per branch and counted void calls; rules keep it.

Part of #953 (item 1).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… errors

check --references accepted a Java action argument naming no parameter
(CE1613 in mxbuild) whenever the script also had a semantic error: the
reference tier was skipped, and a flow's body errors were returned instead of
its reference errors. Both are now reported. call microflow / call nanoflow
arguments are checked against the callee's parameters too (CE1613, measured
on 11.13.0), and an action or flow known to take no parameters reports any
named argument.

Part of #953 (item 7).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…to c23-957

# Conflicts:
#	CHANGELOG.md
#	mdl/linter/report.go
A rule that could not run has no module; ScopeToModules dropped it, so a
--modules report hid the failure that #952 lists separately.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

Describe round trip: delete close page loses ClosePage on write; $[%CurrentUser%] and menu-item show_page do not parse

1 participant