Authored, validated, built, served — and silently invisible. ListViewSchema.description on a per-list-view entry (listViews.<name>.description) never reaches the renderer that has code to display it.
Measured
On a real app (ai.objectstack.duly, booted objectstack dev, console at /_console/apps/duly_app/duly_task/view/by_unit), with this authored on the by_unit list view:
by_unit: {label: 'By business unit',type: 'grid',description: 'Open and in-progress work only. Group counts are computed over the loaded page — …',
…
}The value survives every layer up to the browser:
| layer | carries description? |
|---|
dist/objectstack.json (built artifact, /views/0/listViews/by_unit) | ✅ |
GET /api/v1/meta/view/duly_task | ✅ |
rendered DOM ([data-testid="view-description"]) | ❌ absent |
anywhere in document.documentElement.outerHTML, incl. title attributes | ❌ absent |
So the server is right and the renderer is willing — packages/plugin-list/src/ListView.tsx already has the branch:
{schema.description&&(schema.appearance?.showDescription!==false)&&(<div…data-testid="view-description">{typeofschema.description==='string' ? schema.description : ''}</div>)}Where it is lost
packages/app-shell/src/views/ObjectView.tsx, in renderListView. fullSchema is built by spreading the object'slistSchema and then relaying selected keys off the active viewDef:
constfullSchema: ListViewSchema={
...listSchema,label: viewDef.label??listSchema.label,rowHeight: viewDef.rowHeight??listSchema.rowHeight,sort: (viewDefasany).sort??listSchema.sort,filter: (()=>{…})(),
…label, sort, filter, hiddenFields, inlineEdit, color, allowExport and others are each relayed. description is not, so schema.description can only ever be the object-level list view's description — a per-view one is unreachable.
Why it is worth a card
This is the "declared and inert" shape: nothing errors, the authoring gates (validate, typecheck, test, build) all pass, the API serves the value, and the only symptom is that the sentence the author wrote for the user is not on the screen. An author has no way to notice short of diffing the DOM.
It bites hardest where it is most wanted: a view description is the natural place to disclose a caveat about the view itself (scope, staleness, "this lens is for browsing, the dashboard is authoritative"). That is precisely the text that silently goes missing.
Two notes for whoever picks this up
- A second bug sits one line below the relay.
ListView.tsx renders typeof schema.description === 'string' ? schema.description : '' — but ListViewSchema.description is I18nLabelSchema, i.e. string | { en, 'zh-CN' }. An inline locale map renders as empty string, which is the same silent-blank failure by a different route. Worth fixing in the same pass, or the fix only works for plain strings. appearance.showDescription defaults to true, so no author opt-in is missing here — the value simply never arrives.
Found while scoping a grouped grid in objectstack-ai/duly#86 (the view description was the place the card asked for a residual caveat to be stated for users). Not filing a fix from that side — the app metadata is correct as authored.
Related, same family (authored view metadata not reaching the renderer): #7179 (grid grouping fields absent from the query projection), #7189 (grid grouping and per-group counts computed over the fetched page).
Authored, validated, built, served — and silently invisible.
ListViewSchema.descriptionon a per-list-view entry (listViews.<name>.description) never reaches the renderer that has code to display it.Measured
On a real app (
ai.objectstack.duly, bootedobjectstack dev, console at/_console/apps/duly_app/duly_task/view/by_unit), with this authored on theby_unitlist view:The value survives every layer up to the browser:
description?dist/objectstack.json(built artifact,/views/0/listViews/by_unit)GET /api/v1/meta/view/duly_task[data-testid="view-description"])document.documentElement.outerHTML, incl.titleattributesSo the server is right and the renderer is willing —
packages/plugin-list/src/ListView.tsxalready has the branch:Where it is lost
packages/app-shell/src/views/ObjectView.tsx, inrenderListView.fullSchemais built by spreading the object'slistSchemaand then relaying selected keys off the activeviewDef:label,sort,filter,hiddenFields,inlineEdit,color,allowExportand others are each relayed.descriptionis not, soschema.descriptioncan only ever be the object-level list view's description — a per-view one is unreachable.Why it is worth a card
This is the "declared and inert" shape: nothing errors, the authoring gates (
validate,typecheck,test,build) all pass, the API serves the value, and the only symptom is that the sentence the author wrote for the user is not on the screen. An author has no way to notice short of diffing the DOM.It bites hardest where it is most wanted: a view description is the natural place to disclose a caveat about the view itself (scope, staleness, "this lens is for browsing, the dashboard is authoritative"). That is precisely the text that silently goes missing.
Two notes for whoever picks this up
ListView.tsxrenderstypeof schema.description === 'string' ? schema.description : ''— butListViewSchema.descriptionisI18nLabelSchema, i.e.string | { en, 'zh-CN' }. An inline locale map renders as empty string, which is the same silent-blank failure by a different route. Worth fixing in the same pass, or the fix only works for plain strings.appearance.showDescriptiondefaults totrue, so no author opt-in is missing here — the value simply never arrives.Found while scoping a grouped grid in
objectstack-ai/duly#86(the view description was the place the card asked for a residual caveat to be stated for users). Not filing a fix from that side — the app metadata is correct as authored.Related, same family (authored view metadata not reaching the renderer): #7179 (grid
groupingfields absent from the query projection), #7189 (grid grouping and per-group counts computed over the fetched page).