Measured on @objectstack/cli 17.2.0 from a real app (objectstack-ai/duly).
Reproduction
A nav entry in App.create({ navigation }):
{id: 'nav_schedule',type: 'object',objectName: 'duly_task',viewName: 'schedule',label: 'Schedule',icon: 'gantt-chart'}Mutate viewName: 'schedule' → viewName: 'A4_no_such_view' (confirmed on disk: injected 1, remaining 0), leaving the object and every view declaration untouched:
os validate --json → { "valid": true, "warnings": [], "errors": null }
No error, no warning. os build is green too.
Why it is not harmless
AppNavigationItemSchema.viewName is documented as "Default list view to open. Defaults to 'all'." — so an unresolvable name does not fail, it falls back. The nav item keeps its authored label and icon and opens a different view: the "Schedule" entry opens the plain grid. Nothing anywhere says the named view was not found, and the failure is invisible in review because the label still reads correctly.
This is the same class the platform already gates one door over: #2554's lintViewRefs exists because "a type:'form' action target naming a missing or LIST view opens a broken form at runtime". Navigation is the other, more-travelled reference into the same listViews namespace, and it is unchecked.
It also bites hardest exactly where views are being added — a rename of a list view leaves every nav entry pointing at it silently degraded, with all four gates green.
Suggested fix
Extend lintViewRefs (packages/lint/src/lint-view-refs.ts) to walk app.navigation recursively (type: 'group' children included) and, for each item with both objectName and viewName, resolve the pair against the stack's views:
objectName names no object in the stack → error (probably already covered elsewhere; worth confirming);viewName names no entry in that object's listViews (and is not the list default) → error, with the available view names listed the way REACT_CHART_FIELD_UNKNOWN lists object fields.
Same treatment for the runAction / recordId variants if they carry equivalent unresolvable references.
Workaround in the meantime
objectstack-ai/duly's test/views.test.ts asserts every nav viewName resolves against the declared listViews. Written to be deleted when this ships.
Measured on
@objectstack/cli17.2.0 from a real app (objectstack-ai/duly).Reproduction
A nav entry in
App.create({ navigation }):Mutate
viewName: 'schedule'→viewName: 'A4_no_such_view'(confirmed on disk: injected 1, remaining 0), leaving the object and every view declaration untouched:No error, no warning.
os buildis green too.Why it is not harmless
AppNavigationItemSchema.viewNameis documented as "Default list view to open. Defaults to 'all'." — so an unresolvable name does not fail, it falls back. The nav item keeps its authored label and icon and opens a different view: the "Schedule" entry opens the plain grid. Nothing anywhere says the named view was not found, and the failure is invisible in review because the label still reads correctly.This is the same class the platform already gates one door over: #2554's
lintViewRefsexists because "atype:'form'action target naming a missing or LIST view opens a broken form at runtime". Navigation is the other, more-travelled reference into the samelistViewsnamespace, and it is unchecked.It also bites hardest exactly where views are being added — a rename of a list view leaves every nav entry pointing at it silently degraded, with all four gates green.
Suggested fix
Extend
lintViewRefs(packages/lint/src/lint-view-refs.ts) to walkapp.navigationrecursively (type: 'group'children included) and, for each item with bothobjectNameandviewName, resolve the pair against the stack's views:objectNamenames no object in the stack → error (probably already covered elsewhere; worth confirming);viewNamenames no entry in that object'slistViews(and is not thelistdefault) → error, with the available view names listed the wayREACT_CHART_FIELD_UNKNOWNlists object fields.Same treatment for the
runAction/recordIdvariants if they carry equivalent unresolvable references.Workaround in the meantime
objectstack-ai/duly'stest/views.test.tsasserts every navviewNameresolves against the declaredlistViews. Written to be deleted when this ships.