Skip to content

check --references: refuse bare bindings ALTER PAGE inserts where no entity is in scope - #693

Merged
ako merged 3 commits into
mainfrom
fix/alter-page-unscoped-bindings-check
Sep 25, 2026
Merged

ako merged 3 commits into
mainfrom
fix/alter-page-unscoped-bindings-check

Conversation

@ako

@ako ako commented Sep 25, 2026

Copy link
Copy Markdown
Owner

Follow-up to #678 and #685.

Symptom

On a copy of the stock 11.13.0 project:

alter page FeedbackModule.ShareFeedback_Logo {
  insert after textBox1 { image zzImg (ImageType: imageUrl, ImageUrl: '{1}', ImageUrlParams: [{1} = ImageB64]) }
}

mxcli check --references passed. The enclosing data view's nanoflow (DS_FeedbackForm) isn't in the project, so there is no entity to qualify ImageB64 against, and exec builds a bare attribute reference:

CREATE PAGE catches the same thing at check time (#678). ALTER didn't.

How ALTER finds the entity at the insertion point

At exec, ALTER opens the stored page (OpenPageForMutation) and asks the stored document:

  • INSERT BEFORE/AFTER and REPLACE: the nearest enclosing data source (EnclosingEntity).
  • INSERT INTO: the target container's own data source (EnclosingEntityForChildren).
  • Flow data sources: if that gives nothing, the flow's return entity (EnclosingDataSourceFlow, then getMicroflowReturnEntityName / getNanoflowReturnEntityName).

When the flow is missing, or there is no data container at all, the builder writes a bare name as given.

Fix

  • One lookup: that resolution is now a single function, alterEntityContext, used by both exec (INSERT and REPLACE) and the new check, so they can't disagree.
  • One binding walk: the walk moves out of unscopedBindings into bindingsWithoutScope, shared by CREATE PAGE and the new check.
  • New check validate_alter_unscoped.go, in the --references pass after the ALTER … SET dry run (which already opens the stored page read-only). It reports an error naming the widget, the binding and the missing flow ("or no data container"), and says to qualify it (Module.Entity.Attribute).
  • Skipped, to avoid blocking scripts that work: pages the script itself creates; targets the stored page doesn't have yet; DataGrid 2 column and list-view template inserts; flows the script defines with an entity return type (their return type is used).

Evidence

Control. With the check forced to see an entity, the three failure tests fail ("got 0 errors, want 1"). The seven must-not-flag cases pass either way: entity known, qualified binding, nested data view, $Param/… path, unknown target, flow defined in the script.

Real runs (copies of the project):

run result
the statement above before: check passed. Now: reference error naming image zzImg, ImageUrlParams {1} = ImageB64; exec refused at its pre-check
qualified variant (FeedbackModule.Feedback.ImageB64) check passes, exec works, mx check 0 errors
insert into dataView1 { textbox t (Attribute: FullName) } on Administration.Account_Edit passes unflagged, execs

No data container at all, measured with the pre-fix binary. Both are now reported at check time:

  • an image parameter {1} = FullName before layoutGrid1 is written bare, and the project can't be loaded;
  • a text box Attribute: FullName at top level is dropped silently, and the build fails with CE7005.

Bug-test mdl-examples/bug-tests/alter-page-unscoped-insert-bindings.mdl.

Not covered

  • Same-script pages: a page created and then altered in one script is skipped, because it isn't stored yet. So make check-mdl on the bug-test proves only that it parses; the real check needs a second run against the saved page.
  • Fragments: widgets that come in via use fragment inside an INSERT aren't expanded at check time.
  • Column inserts: exec's DataGrid 2 column path doesn't resolve flow-sourced entities like the other paths do, so the check skips it.

Checklist

  • Failing tests first
  • Control recorded
  • Real runs on copies, including mx check
  • Bug-test MDL; make check-mdl passes
  • Finding appended; make check-findings passes
  • make build && make test && make lint pass, each exit code checked separately

🤖 Generated with Claude Code

ako and others added 3 commits September 25, 2026 14:03
…s in scope

`alter page … { insert after textBox1 { image … ImageUrlParams: [{1} = ImageB64] } }`
on Feedback's ShareFeedback_Logo, whose data view is sourced by a nanoflow the
project lacks, passed `check --references`; exec then wrote a bare
AttributeRef and `mx check` could not load the project. A widget inserted
outside every data container fails the same way (a text box's Attribute is
dropped instead: CE7005).

ALTER's entity context lives in the stored document, so `check --references`
now opens it (read-only, as the ALTER … SET dry run already does) and resolves
the insertion point's entity through alterEntityContext — lifted out of the
INSERT/REPLACE exec paths so check and exec share it. With no entity, the bare
bindings of the inserted widgets are reported via bindingsWithoutScope, the
walk lifted out of CREATE PAGE's unscopedBindings (#678).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ako
ako merged commit 7df73ce into main Sep 25, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant