You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
mode and size carry closed vocabularies. view is an unconstrained string with no vocabulary and no resolution step, so any value authors clean and validates clean.
What it actually does
Its only read in the shipped console is the record-navigation hook, which puts it in the second argument of onNavigate — the slot that otherwise carries the literal navigation-mode string:
framework-BfSv4Kb0.js
function wc(e){ let {navigation:t, objectName:n, onNavigate:r, onRowClick:i}=e,
l = t?.mode ?? `page`,
d = t?.view,
…
if (l === `page`) { let t = e.id||e._id; r && t != null && r(t, d ?? `view`); return }
So an authored view does not select a view — it substitutes for the mode token. And at least one consumer in the same bundle reads that argument against a closed two-value vocabulary:
index-BU-FzlQ6.js
onNavigate: (e,t) => { if (t === `edit`) … else if (t === `view`) { navigate to the record page } }
Any authored value other than edit or view matches neither branch there. Other consumers in the bundle take one argument and ignore the second, which is why the effect is invisible on some grids and would be a dead row click on others.
The original finding enumerated the bundle rather than sampling it: every.view property read (exactly three — this one, one on a dataSource, one in mermaid) and everyformViews read (formViews?.default in RecordFormPage, useActionModal and RecordDetailView subforms, plus one write in the stored-view normalizer). No read anywhere is keyed by an authored view name. So there is no path by which navigation.view could resolve a named form view, and none by which any other key could either.
Why this is worth a card rather than a doc fix
The name is the trap. view, sitting in a bag next to mode / size / width, reads as "which view to open" — that is exactly how the downstream app authored it, with the comment // Use named form view beside it. It validated, it built, and it selected nothing. This is the ADR-0049 declared-but-unenforced shape, with the extra sharpness that the key is not merely ignored: it is consumed, in a slot that means something else.
Same class as #14108 (app navigation viewName never resolved against the target object's listViews), which shipped a fix in #14286 by extending lintViewRefs. That precedent is why this is filed rather than absorbed: navigation is the well-travelled reference into a view namespace, and this is a second door into it that nothing checks.
What the downstream app did, and did not do
objectstack-ai/hotcrm deleted its one occurrence — a browser reading confirmed the row click was unaffected there, so the key was inert on that grid rather than breaking it. The app has not compensated for the platform behaviour in any other way, and should not: this is a platform surface.
Remove — retire navigation.view from ListViewSchema.navigation under ADR-0049. If the mode slot genuinely wants an author-supplied override, it wants a key named for what it does and a closed vocabulary, not a free string named view.
Whichever is chosen, the current state — an unconstrained string whose name promises view selection and whose only read feeds the mode argument — is the one state that teaches authors something false.
Measured on
@objectstack/spec17.3.0 and the pinned@objectstack/console17.3.0 bundle, from a real app (objectstack-ai/hotcrm, issue #1716).The key
ListViewSchema.navigationdeclaresviewalongside the keys that describe navigation behaviour:modeandsizecarry closed vocabularies.viewis an unconstrained string with no vocabulary and no resolution step, so any value authors clean and validates clean.What it actually does
Its only read in the shipped console is the record-navigation hook, which puts it in the second argument of
onNavigate— the slot that otherwise carries the literal navigation-mode string:So an authored
viewdoes not select a view — it substitutes for the mode token. And at least one consumer in the same bundle reads that argument against a closed two-value vocabulary:Any authored value other than
editorviewmatches neither branch there. Other consumers in the bundle take one argument and ignore the second, which is why the effect is invisible on some grids and would be a dead row click on others.The original finding enumerated the bundle rather than sampling it: every
.viewproperty read (exactly three — this one, one on a dataSource, one in mermaid) and everyformViewsread (formViews?.defaultinRecordFormPage,useActionModalandRecordDetailViewsubforms, plus one write in the stored-view normalizer). No read anywhere is keyed by an authored view name. So there is no path by whichnavigation.viewcould resolve a named form view, and none by which any other key could either.Why this is worth a card rather than a doc fix
The name is the trap.
view, sitting in a bag next tomode/size/width, reads as "which view to open" — that is exactly how the downstream app authored it, with the comment// Use named form viewbeside it. It validated, it built, and it selected nothing. This is the ADR-0049 declared-but-unenforced shape, with the extra sharpness that the key is not merely ignored: it is consumed, in a slot that means something else.Same class as #14108 (app navigation
viewNamenever resolved against the target object'slistViews), which shipped a fix in #14286 by extendinglintViewRefs. That precedent is why this is filed rather than absorbed: navigation is the well-travelled reference into a view namespace, and this is a second door into it that nothing checks.What the downstream app did, and did not do
objectstack-ai/hotcrmdeleted its one occurrence — a browser reading confirmed the row click was unaffected there, so the key was inert on that grid rather than breaking it. The app has not compensated for the platform behaviour in any other way, and should not: this is a platform surface.Suggested disposition — enforce or remove
Either honest treatment closes it:
navigation.viewagainst the target object'sformViewsat lint/build time (the fix(lint): resolve app navigationviewNameagainst the target object's list views #14286 shape), and give the runtime a real read for it, so the key means what its name says.navigation.viewfromListViewSchema.navigationunder ADR-0049. If the mode slot genuinely wants an author-supplied override, it wants a key named for what it does and a closed vocabulary, not a free string namedview.Whichever is chosen, the current state — an unconstrained string whose name promises view selection and whose only read feeds the mode argument — is the one state that teaches authors something false.
Generated by Claude Code