Skip to content

[finding] rowColor.colors is optional in the schema but required by the only renderer — rowColor: { field } alone is a parse-clean no-op #14111

Description

@os-warren

Noticed while deciding how to colour list rows consistently with a status field (objectstack-ai/duly #12). Not blocking anything — we chose not to author rowColor at all — but it is an ADR-0078 functional-completeness shape that the existing rule family does not cover.

The shape

RowColorConfigSchema (spec/src/ui/view.zod.ts):

field:  z.string().describe('Field to derive color from (typically a select/status field)'),
colors: z.record(z.string(), z.string()).optional().describe('Map of field value to color (hex/token)'),

field required, colors optional. The .describe() on field"derive color from" — reads as though the derivation happens without a map, which is what a select field's own options[].color would let it do.

The grid renderer disagrees (plugin-grid, 17.2.0):

function qt(e){ return useCallback(t => {
  if (!e?.field || !e.colors) return;        // ← both, or nothing
  let n = String(t[e.field] ?? ``), r = e.colors[n];
  if (r) return Kt(r);
}, [e?.field, e?.colors]) }

So rowColor: { field: 'status' } parses, publishes, and colours nothing. No error, no warning, no console message.

Why it belongs with the existing rules

This is the same family validateFunctionalCompleteness already gates: a summary with no summaryOperations, a lookup with no reference, a select with no options, a calendar with no calendar block. Every key is known, so #4001's unknown-key rejection cannot see it and the liveness ledger cannot either — which is precisely the gap that rule exists to fill.

Two ways to close it, and they are not equivalent

  • A — gate it. Add view/row-color-without-colors (warning) to checkViewCompleteness: a rowColor with a field and no colors never colours a row. Cheap, matches the existing rules exactly.
  • B — make the description true. Let the renderer fall back to the bound field's options[].color when colors is absent, the way plugin-timeline already resolves its colorField. This is the better outcome for the case the key is obviously for — colouring by a status select — because it removes the need to hand-copy the option colours into a second map that then drifts from the object. An author who copies them has two sources of truth for one palette.

If B, A is still worth keeping for the non-select case (a text or number field with no options has nothing to derive from).

Activity

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

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions