Repository navigation
fix(ash_ui validations): trust custom:* opaque widget types in :list binding allow-list - #113
Merged
ty13r merged 1 commit intoMay 18, 2026
Conversation
…t binding allow-list
Authoring validator (`validate_element_binding_locality!` at
lib/ash_ui/resources/validations/authoring.ex:373) raises ArgumentError
"does not expose collection semantics" for any :list-typed binding on
Elements whose canonical widget_type isn't in @list_widgets ("list",
"table", "info_list", "select", "list_repeat").
`custom:*` opaque widget types are application-extension primitives
outside the canonical catalog (per Storage.canonical_widget_type at
lib/ash_ui/dsl/storage.ex:162). The validator can't know their
collection semantics and shouldn't gate the :list binding allow-list
on them; trust the authoring intent.
Conservative scope:
- Bypass applies ONLY to "custom:*" string widget_types
- nil widget_type (`ui_element` block missing entirely) still
raises through downstream missing-element error paths
- Canonical list-supporting widgets (`list`, `table`, etc.) remain
the exclusive allow-list for non-custom Elements; row-template
primitives like `:artifact_row` continue to belong INSIDE a
`:list_repeat` parent that owns the list binding (per the
Phase31RepeatListElement canonical pattern + Codex P2 #2 review)
Triggered by Wave AshUI-3.9 Item 2 dispatch (2026-05-17/18). The
original sizing investigation proposed adding `artifact_row` to
@list_widgets so destination Elements could hold :list bindings
directly; Codex P2 #2 review correctly pointed out that
:list_repeat is the canonical destination wrapper (not :artifact_row)
per the Phase31RepeatListElement test fixture. Per umbrella CLAUDE.md
"substrate gaps become our roadmap, not asks-of-Pascal" — the
`custom:*` bypass DOES belong here since the canonical catalog has
no opinion on opaque application types.
Per Codex P2 #3 review: the bypass is tightened to ONLY match
`"custom:*"` binary strings. Earlier draft included `is_nil(widget_type)`
which would have silently accepted bindings on Elements missing their
`ui_element` declaration entirely; that case should still surface
as a downstream missing-element error.
Test coverage:
- Existing negative test (`:text` widget rejecting :list) still passes
(regression check that the allow-list still gates unknown-bad types)
- New positive test: `"custom:thread_card"` widget compiles with
`binding_type :list`
The positive test uses a helper `assert_validator_accepts_list_binding`
that checks the SPECIFIC validator error is not raised; downstream
Ash domain-registration exceptions (which fire only AFTER the validator
we're testing passes) are acknowledged as proof the validator accepted.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ty13r
force-pushed
the
claude/validator-allow-artifact-row-and-custom-widgets
branch
from
May 18, 2026 02:43
35a38e3 to
b206a66
Compare
Member
Author
Codex r2 disposition — false-positive on the
|
5 of 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.
Summary
Allows
:listbinding_type on"custom:*"opaque widget types invalidate_element_binding_locality!atlib/ash_ui/resources/validations/authoring.ex:373.(Scope narrowed in r2 per Codex P2 #2 + #3 review.) Original draft also added
"artifact_row"to@list_widgets; Codex correctly pointed out that:list_repeatis the canonical destination wrapper for collection hydration (per thePhase31RepeatListElementtest fixture), NOT:artifact_row. Row-template primitives like:artifact_rowcontinue to belong INSIDE a:list_repeatparent that owns the list binding.Why
The validator currently raises
ArgumentError "does not expose collection semantics"for any:list-typed binding on Elements whose canonical widget_type isn't in@list_widgets("list","table","info_list","select","list_repeat")."custom:*"opaque widget types are application-extension primitives outside the canonical catalog (perStorage.canonical_widget_typeatlib/ash_ui/dsl/storage.ex:162). The validator can't know their collection semantics and shouldn't gate the:listbinding allow-list on them; trust the authoring intent.Per Codex P2 #3 the bypass is tightened to ONLY match
"custom:*"binary strings; nil widget_type (ui_elementblock missing entirely) still raises through downstream missing-element error paths.Triggering precedent
Wave AshUI-3.9 Item 2 dispatch (2026-05-17/18). Ariston-ui attempted to add
ui_bindings do binding_type(:list) endblocks to 11 destination Element modules perdocs/iur-missing-list-binding-sizing.md. The sizing investigation got the architectural premise wrong (proposed:artifact_rowas destination); the correct pattern is:list_repeatwrapping the row template (perPhase31RepeatListElement). Ariston-side fix becomes a structural restructure rather than a flatui_bindingsadd. This PR'scustom:*bypass is the only ash_ui-side gap that's truly Pascal-roadmap material.Per umbrella
CLAUDE.md"substrate gaps become our roadmap, not asks-of-Pascal" — thecustom:*bypass DOES belong here since the canonical catalog has no opinion on opaque application types.Files
lib/ash_ui/resources/validations/authoring.ex—validate_element_binding_locality!gets acustom_widget?bypass for"custom:*"widget types (binary-prefix check only)test/ash_ui/resource/locality_test.exs— 1 new positive test for"custom:thread_card"; helperassert_validator_accepts_list_binding/1for the validator-passed-but-downstream-domain-error patternTest plan
mix test test/ash_ui/resource/locality_test.exs— 5/0 pass (4 original + 1 new):textwith:list)"custom:thread_card"widget compiles withbinding_type :listCross-references
docs/iur-missing-list-binding-sizing.md(PR #385); doc being updated in a follow-up to reflect the architectural premise correction:list_repeatrestructure verifies whether the gap is moot or narrower (custom:*-only)Scope notes
artifact_rowextension dropped;custom:*bypass tightenedStorage.canonical_widget_typeresolves types🤖 Generated with Claude Code