Routed from objectstack-ai/cloud#2085 by the repo:cloud PM seat. The cloud half was measured and is legitimate; the defect is here, and the analysis below is the cloud seat's — ⚠️ re-measure before acting on it, it has not been re-established in this repo.
⚠️ This card needs objectui triage (domain:* + a pm:* state). I am not this repo's PM seat and have deliberately not stamped its taxonomy.
What the user sees
A pet-clinic app built by the cloud AI build flow renders each kanban card with its title printed twice:
小虎犬牙洁治评估 card title (resolves to visit_title)
小虎犬牙洁治评估 first body row (visit_title is also in config.kanban.columns)
可乐
2026/9/7 上午9:30
Why it is not the producer's fault
The originating cloud card first read this as lost nameField metadata. That was measured and refuted: the served object metadata does carry nameField, re-derived to exactly the field the blueprint author declared, on getMetaItem (draft and active), getMetaItems and engine.registry.getObject.
And the producer emitting the title field among the card columns is correct behaviour: viewBody emits config.kanban = { groupByField, columns } where columns default to the object's fields. The renderer is supposed to skip whichever field it just used as the card title, and it has the machinery to do it.
The defect
packages/plugin-kanban/src/ObjectKanban.tsx
:418 const nameFieldKey: string | undefined = objectDef?.NAME_FIELD_KEY;
:542 const titleFieldsToSkip = new Set([
...(explicitTitleField ? [explicitTitleField] : []),
...(nameFieldKey ? [nameFieldKey] : []),
'name', 'full_name', 'title', 'subject', 'display_name',
]);
:568 for (const f of explicitCardFields) { if (titleFieldsToSkip.has(f)) continue; … }
NAME_FIELD_KEY is the last and most deprecated rung of this repo's own ADR-0079 ladder:
// packages/core/src/utils/record-title.ts:321
return objectDef?.nameField ?? objectDef?.displayNameField ?? objectDef?.NAME_FIELD_KEY;
The served metadata carries nameField and no NAME_FIELD_KEY, so nameFieldKey is undefined, the skip set collapses to the five hard-coded literals, none of which is visit_title, and the field renders again as a body row.
⭐ The same card gets its title right, because that path calls getRecordDisplayName, which walks the whole ladder. One reader was converted, the other was not — which is precisely the hazard resolveNameField's own docblock names:
One spelling, two readers — the value-space resolver and the name-space resolveNameField. Re-typing the ?? chain at the second reader is how the two would drift into disagreeing about which field titles an object.
The fix and its precedent
resolveNameField is exported from @object-ui/core for exactly this (record-title.ts:346). plugin-detail already delegates to it (synth/buildDefaultPageSchema.ts:431, objectui#7287). plugin-kanban's skip set is the reader that was missed.
ObjectCalendar does not share the defect — it has no card-body skip set and uses getRecordDisplayName only. ⚠️ That is a claim from the cloud seat's reading; worth re-checking, along with whether any other plugin re-types the chain.
Suggested acceptance
A kanban whose object declares nameField and whose view columns include that field renders the title once. ⚠️ And the negative control that matters: an object that genuinely has only the legacy NAME_FIELD_KEY spelling must keep working — the ladder exists because both spellings are still in the wild.
Origin: objectstack-ai/cloud#2085 (split from cloud#2073, where the nameField premise was measured and refuted).
https://claude.ai/code/session_01Gp1JypWKsxjpn1wb2JdqAY
Routed from objectstack-ai/cloud#2085 by the⚠️ re-measure before acting on it, it has not been re-established in this repo.
repo:cloudPM seat. The cloud half was measured and is legitimate; the defect is here, and the analysis below is the cloud seat's —domain:*+ apm:*state). I am not this repo's PM seat and have deliberately not stamped its taxonomy.What the user sees
A pet-clinic app built by the cloud AI build flow renders each kanban card with its title printed twice:
Why it is not the producer's fault
The originating cloud card first read this as lost
nameFieldmetadata. That was measured and refuted: the served object metadata does carrynameField, re-derived to exactly the field the blueprint author declared, ongetMetaItem(draft and active),getMetaItemsandengine.registry.getObject.And the producer emitting the title field among the card columns is correct behaviour:
viewBodyemitsconfig.kanban = { groupByField, columns }where columns default to the object's fields. The renderer is supposed to skip whichever field it just used as the card title, and it has the machinery to do it.The defect
packages/plugin-kanban/src/ObjectKanban.tsxNAME_FIELD_KEYis the last and most deprecated rung of this repo's own ADR-0079 ladder:The served metadata carries
nameFieldand noNAME_FIELD_KEY, sonameFieldKeyisundefined, the skip set collapses to the five hard-coded literals, none of which isvisit_title, and the field renders again as a body row.⭐ The same card gets its title right, because that path calls
getRecordDisplayName, which walks the whole ladder. One reader was converted, the other was not — which is precisely the hazardresolveNameField's own docblock names:The fix and its precedent
resolveNameFieldis exported from@object-ui/corefor exactly this (record-title.ts:346).plugin-detailalready delegates to it (synth/buildDefaultPageSchema.ts:431, objectui#7287).plugin-kanban's skip set is the reader that was missed.ObjectCalendardoes not share the defect — it has no card-body skip set and usesgetRecordDisplayNameonly.Suggested acceptance
A kanban whose object declares⚠️ And the negative control that matters: an object that genuinely has only the legacy
nameFieldand whose view columns include that field renders the title once.NAME_FIELD_KEYspelling must keep working — the ladder exists because both spellings are still in the wild.Origin: objectstack-ai/cloud#2085 (split from cloud#2073, where the
nameFieldpremise was measured and refuted).https://claude.ai/code/session_01Gp1JypWKsxjpn1wb2JdqAY