Refuse a bare attribute reference on every write, ALTER PAGE included - #685
Merged
Merged
Conversation
…ncluded
`alter page FeedbackModule.ShareFeedback_Logo { insert after textBox1 {
image zzImg (ImageType: imageUrl, ImageUrl: '{1}', ImageUrlParams:
[{1} = ImageB64]) } }` reported "Altered page" and left a project `mx check`
could not LOAD (ArgumentNullException setting 'Attribute', 11.13.0). The data
view's nanoflow is missing, so nothing qualifies `ImageB64`, and the
pluggable widget's template-parameter builder writes the name as given.
The refusal from #678 sat in encodePage/encodeSnippet. ALTER PAGE patches
the stored BSON in the page mutator and saves it through UpdateRawUnit, so it
never reached that check.
- The check moves to modelsdk/canon (BareAttributeRefError). The writer's
updateUnit and insertUnit call it next to DuplicateElementIDError, so every
raw write is covered: ALTER, styling, widget sync, layouts, templates.
- encodePage/encodeSnippet keep calling it first, so a CREATE refusal still
names the page and not the unit id.
- It refuses every bare reference, stored ones too. Across all 374 units of
a stock 11.13 project, 73 of 73 AttributeRefs are qualified: 71 in pages,
1 in a snippet, 1 in a page template, none in any other unit type. Studio Pro
cannot load a bare one either, so no project it saved can hold one.
Real run on a copy of that project: the ALTER above is refused, naming
"ImageB64" and zzImg's path, and no unit changes. The qualified form, `set
Title`, a qualified textbox insert and a Class change all apply, and
`mxcli docker check` reports 0 errors. Control: with the writer guard
stubbed, both new tests fail with the reported symptom.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ttributeref-guard
6 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #678.
Symptom
On a copy of the stock 11.13.0 project, this statement printed "Altered page":
The stored page then held
DomainModels$AttributeRef { Attribute: "ImageB64" }, andmx checkcould not load the project:The data view's nanoflow is missing, so there is no entity to qualify
ImageB64against.Cause
#678 added a guard against bare attribute references, but only in
encodePageandencodeSnippet, the CREATE path. ALTER PAGE and ALTER SNIPPET write through the page mutator andUpdateRawUnit, which the guard never saw. The bare name comes from the template-parameter builders inmdl/backend/widgetobj/builder.go(pluggable widgets and DataGrid2 columns), which copy the name through as given.Fix
The guard moves to the one choke point every unit write goes through:
modelsdk/mprWriter.updateUnitandinsertUnit, right after the existing duplicate-$IDguard.canon.BareAttributeRefErrorinmodelsdk/canon/attributeref.go; the oldmdl/backend/modelsdk/page_bare_attributeref.gois removed.encodePageandencodeSnippetstill check first, so a CREATE refusal names the page rather than a unit id.Stored models are unaffected. Across all 374 units of the test project there are 73 AttributeRefs (71 in pages, 1 in a snippet, 1 in a page template), and none is bare. Studio Pro cannot load a bare one, so no project it saved holds one; the guard cannot block an ALTER of anything stored. The refusal names the ref's full path, so the same ALTER can drop or qualify it.
Evidence
Control. With the writer guard stubbed:
TestUpdateUnitRefusesABareAttributeRef: "write accepted a unit holding the bare attribute reference "Name"";TestAlterPageSaveRefusesABareAttributeRef: "ALTER saved a page holding a bare attribute reference".Real run (fresh copy of the project):
"ImageB64" at …/dataView5.Widgets/zzImg.Object/…/TextTemplate/Parameters/AttributeRef. Zero.mxunitfiles changed (sha256 before/after).set TitleonAdministration.Account_Edit; inserting a qualified textbox;set Classon it.mxcli docker check: 0 errors.Bug-test
mdl-examples/bug-tests/alter-page-bare-attributeref-refused.mdl: runs clean; its bare variant is refused.docs-wiki/bug-patterns/unloadable-model-writes.mdsaid an attribute reference could be "bare orModule.Entity.Attribute". That's wrong for a stored AttributeRef, so it's corrected.Not fixed here
checkdoesn't catch a bare template parameter in an ALTER, so the refusal appears at exec time.Attribute:intoAttributeRef: nulland reports success: the binding is silently dropped.insertUnithas the guard but no dedicated test.Checklist
mx check0 errorsmake check-mdlpassesmake check-findingspassesmake build && make test && make lintpass, each exit code checked separately🤖 Generated with Claude Code