Skip to content

finding(types): the record:highlights fields[] object arm declares icon, which the spec's strict arm refuses — tsc green, publish RED #9280

Description

@os-tesla

Found while implementing objectui#9187 (same interface, different key
that card is RecordHighlightsComponentProps.layout, this is the fields[]
entry union one member down). Filed rather than carried on that branch because
the evidence balance points the other way and the direction is not triage-able
from the #9187 diff alone — see "Why this is not the same repair" below.

packages/types/src/record-components.ts (⚠️ generic written with SQUARE
brackets — GitHub's body sanitizer eats tag-shaped fragments, fences included):

fields: Array[
  string | { name: string; label?: string; icon?: string; type?: string; readonly?: boolean }
];

@objectstack/spec RecordHighlightsProps.fields[]'s object arm declares
FOUR keys and is $strict. icon is not one of them.

Measured, on the installed pin (17.4.0), against controls

Reading the arm's own shape rather than transcribing anything:

fields[] object-arm keys : ["name","label","type","readonly"]
arm strictness           : $strict  (an unlisted key is refused, not stripped)

The parse probes, one instrument, one loop:

{ fields: [{ name: 'x', label: 'L' }] }     GREEN                            <- CONTROL
{ fields: [{ name: 'x', icon: 'star' }] }   RED   invalid_union at fields.0
{ fields: [{ name: 'x', zzzNonsense: 1 }] } RED   invalid_union at fields.0  <- CONTROL
{ fields: ['x'] }                           GREEN                            <- CONTROL

All three controls fired as they should: a declared key parses green, so the
arm is not refusing everything; an arbitrary key is refused with the SAME code
as icon, so icon is not special-cased; and the string arm is unaffected.

Why it matters

An author writes fields: [{ name: 'amount', icon: 'dollar-sign' }] on a
record:highlights block, tsc is green, and the contract refuses the whole
document at publish. Same class as objectui#9187 and objectui#8604: the
published TypeScript face offers something the only layer that matters rejects.

⚠️ Why this is NOT the same repair as objectui#9187

On objectui#9187 every other layer already agreed with the contract, so
narrowing the declaration was the whole fix. Here the balance is inverted
icon is live on three layers and dead only on the contract:

  • packages/types declares it (the arm quoted above).
  • RecordHighlightsRenderer reads it, in the normalizer that accepts the
    bare-string and object forms.
  • @object-ui/plugin-detail's registry manifest publishes it to authors, in
    the fields input description.

⇒ deleting the declaration alone would leave a renderer read and a published
manifest promise pointing at a key nothing can author. The two candidate
directions are a spec widening (declare icon on the arm upstream) and a
three-layer retirement here. Widening a published accepted set is on the
human floor and is explicitly not this repository's call, so this card is a
measurement plus a fork, not a plan.

⛔ Not claimed, ⛔ not graded — labels, domain:* and priority are triage's.

Dedup — the channel, its control, and its GAP

Read the full open population over repository-scoped REST (5 pages,
per_page=100, 467 items, 455 of them issues) and grepped title plus body: 10
cards mention "highlight", none of them also mentions icon. Known-hit
control fired — objectui#9187 itself is in the listing, so the enumeration
reaches the population rather than returning a confident empty.

⚠️ The closed half was not measured, and that is a gap, not a silence. Both
text-search channels were refused in this session: MCP search_issues returned
API rate limit already exceeded on the shared seat identity, and
repository-scoped REST /search/issues is refused by this container's proxy
(sessions are bound to their configured repositories), which leaves no text
query over closed cards. Nearest open neighbour checked by hand and ruled NOT a
duplicate: objectui#8649 is the opposite direction (renderer reads keys the
TYPE does not declare); this is the type declaring a key the CONTRACT does not.

Refs: objectui#9187 (the sibling key, same interface) · objectui#8604 (same
class, one interface over) · objectui#8649 (opposite direction, same renderers).

Reported by an ObjectStack dev agent seat while working objectui#9187; session
reference https://claude.ai/code/session_01UzHd6hDYatoDn17BuwKxnZ (written as
prose because an attribution footer does not survive issue creation).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdomain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec lanepackage: typespriority:p2

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions