Skip to content

finding(app-shell): AuditFieldDef is a third relationship-target reader — widened carrier type + #6528's legacy spelling chain #6719

Description

@os-sales

Surfaced by the carrier census in objectui#6648 (PR #6718), deliberately left out of that PR's scope: it is a third file, and the spelling half is #6528's axis rather than #6648's.

What the census found

objectui#6528 narrowed two relationship-target resolvers to the spec spelling reference; objectui#6648 narrowed the same two to the string CARRIER. The census run for #6648 walked every reference key in both trees through the TypeScript compiler API and turned up a third reader of the same value that neither pass touched:

packages/app-shell/src/utils/auditHistoryDisplay.ts

/** Minimal structural view of an object field definition. */exportinterfaceAuditFieldDef{
...
reference_to?: string|string[];reference?: string|string[];[k: string]: unknown;}/** Lookup reference target when it is a single concrete object (skip polymorphic). */functionlookupTarget(def: AuditFieldDef|undefined): string|null{consttarget=def?.reference_to??def?.reference;returntypeoftarget==='string'&&target.length>0 ? target : null;}

Two unfounded widenings on a type that documents itself as "a structural view of an object field definition":

  1. Carrier.FieldSchema.reference is a plain z.string(). ObjectSchema.safeParse (spec 17.2.0) REFUSES ['crm_account'] (expected string, received array) and { object: 'crm_account' } (expected string, received object). The census found ZERO producers of either carrier at the field-def key position across both trees, against 587 bare-string carriers there. string[] is a shape nothing declares and nothing emits.
  2. Spelling.reference_to ?? reference reads the legacy spelling FIRST, and reads it ahead of the canonical one. That is the exact chain objectui#6528 measured away in the two resolvers it covered: reference_to is a live key only on ObjectUI's own view/field contract, never on an object metadata document, and ObjectSchema refuses it by name.

Why this is observation-class, not a live defect

lookupTarget already narrows at RUNTIME — typeof target === 'string' && target.length > 0 — so a non-string carrier resolves to null here today. It never takes element zero, and its comment ("skip polymorphic") says the refusal is deliberate. Worth recording: when a third author met the same question the array branch in the other two resolvers was answering, they chose to REFUSE rather than guess, which is corroborating evidence for #6648's removal.

So there is no wrong answer being produced. What is left is a declared surface wider than anything that can reach it, and a spelling preference inverted relative to the spec.

What a fix would be

Filing rather than riding it along on PR #6718: the card there is explicit that its scope is two named files "in one pass", and the spelling half belongs to a different axis with its own census standard.

Generated by Claude Code

Metadata

Metadata

Assignees

Labels

domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpm:dispatchedpriority:p2

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions