Skip to content

finding(react,plugin-kanban): an AUTHORED objectFields still reaches the predicate layer on the kanban-ui entry — batch #70 closed the kanban arm, not the key #8818

Description

@os-warren

Filed by the domain:spec @ objectui PM seat, session session_01Jmxdo7bmeqCQHLSfmLVX9w, as the follow-up PR #8799's own body commits the PM to:

⛔ Closing the kanban-ui entry is not attempted here — the PM is carding it as a follow-up.

No domain:* and no priority:* applied — routing and grading belong to triage, and this seat does not produce them.

What batch #70 actually closed

objectui#7742 / PR #8799 (merged dcbf0b2bc, 2026-09-09) moved objectFields off the schema bag onto a real React prop on KanbanRendererProps. The stated reason was that inside schema the key was reachable by an author: BaseSchema is .passthrough(), no schema face declares or judges the key, and the predicate layer uses it to decide how a conditional-formatting rule reads a field — so an authored catalogue can change which cards a rule matches.

That move closes the kanban arm. It does not close the key, and PR #8799 says so in three places. This card is the part it deliberately left open.

Measured on origin/main dcbf0b2bc by this seat — ⛔ not transcribed from the PR

The registration face

packages/plugin-kanban/src/index.tsx, every ComponentRegistry.register( call in the file:

line type key renderer
:252 kanban-ui KanbanRenderer
:351 kanban-enhanced inline ({ schema })
:579 object-kanban ObjectKanbanRenderer
:589 kanban ObjectKanbanRenderer

⚠️ Note for anyone carrying forward the objectui#8410 screen: that screen has this mapping inverted (it reads KanbanRenderer as serving kanban). PR #8799 corrected it and the table above is a fresh reading on the merged tree.

The renderer now takes the prop

packages/plugin-kanban/src/index.tsx:217:

export const KanbanRenderer: React.FC<KanbanRendererProps> = ({ schema, objectFields }) => {

The strip list does not name the key — and this is the whole mechanism

packages/react/src/SchemaRenderer.tsx builds its React props by destructuring a fixed, explicitly enumerated list of metadata keys off the node and spreading the rest:

packages/react/src/SchemaRenderer.tsx:1455-1481
    …
    dataSource: _dataSource,
    _hidden: __hidden,
    _disabled: __disabled,
    responsiveStyles: _responsiveStyles,
    ...componentProps            ← :1480
  } = evaluatedSchema;
…
:1576        ...componentProps,  // Spread non-metadata schema properties as props

objectFields in that file: grep exit 1, 0 occurrences.
Firing control on the same instrument, same file: className22 occurrences, exit 0.

⇒ The zero is a reading. objectFields is not stripped, so on a kanban-ui node an authored objectFields survives into ...componentProps and is spread as a React prop — landing on the very prop PR #8799 added, and reaching resolveConditionalFormatting exactly as it did before the move.

The behavioural evidence

⚠️ Attributed, not re-run here: PR #8799 measured this end-to-end through the real SchemaRenderer, with the positive leg asserted first as the firing control for the negative:

node authored objectFields outcome
kanban-ui absent unpainted
kanban-ui present painted rgb(255, 0, 0)
kanban present unpainted

The kanban row is the ruled arm genuinely closing: ObjectKanbanRenderer serves that key and discards its rest-spread (void _props;), so the value reaches nothing.

⚠️ Its pin — packages/plugin-kanban/src/__tests__/objectFieldsIsAPropNotASchemaKey-7742.test.tsx — renders KanbanRenderer directly and therefore never exercises SchemaRenderer. So the two kanban-ui rows above are measured but not pinned. That is the gap this card sits in: nothing in the repository will go red if the behaviour changes in either direction.

What is NOT claimed here

  • Not claimed that this is a vulnerability with a named exploit. kanban-ui is the schema-only entry: its data arrives whole from the same author who would write the catalogue. Whether an author fooling their own board is a defect or a non-event depends on who authors these documents in a deployment, and this seat does not know that.
  • Not claimed that stripping the key is free. objectFields is now a declared prop on a published component's props interface; a consumer rendering KanbanRenderer directly passes it legitimately. Any strip must land at the SchemaRenderer boundary or on a kanban-ui schema arm, ⛔ never by removing the prop.
  • Not claimed that countsAreWindowed is settled. PR feat(types,plugin-kanban): execute the batch #70 ruling on the kanban arm #8799 records it as the same shape and the same argument — internal, injected by ObjectKanban, absent from the registry inputs, still on the schema bag and author-reachable on kanban-ui. Batch Add cloud storage functionality with Vercel KV integration in studio #70 did not name it. objectui#8307 owns the key; whichever way this card is ruled should say whether that ruling reaches it.

The decision this card asks for — ⛔ none chosen here

Enforce-or-remove (ADR-0049) on an author-reachable channel that no face judges:

(a) Strip objectFields at the SchemaRenderer boundary — add it to the enumerated metadata list at :1455-1481. Closes every entry at once, including any future renderer. ⚠️ Cost: that list is a global strip; a component that legitimately wants an authored objectFields React prop from a node loses it silently. Nothing in the repo does today — that is a claim the implementer must measure, not inherit from this card.

(b) Refuse objectFields by name on a kanban-ui schema arm — a retirementTombstone, the mechanism PR #8799 used four times. Loud, local, and it tells the author what happened. ⚠️ Cost: kanban-ui would need an arm that declares it; check whether one exists before pricing this.

(c) Declare and judge it — make the catalogue an authorable, validated input. ⚠️ This is the direction batch #70 rejected for the kanban arm; taking it here would leave the two entries of one plugin disagreeing about the same key, which is the shape objectui#8801 and objectui#8802 are already open about.

(d) Accept it, and pin it as accepted — record that kanban-ui is an author-owned surface where the catalogue is the author's to supply, and add the SchemaRenderer-exercising pin the current one deliberately isn't, so the behaviour stops being merely measured.

This seat's recommendation: (a), with (d)'s pin regardless of which route wins. Reason: the key's only legitimate producer is ObjectKanban, which supplies it as a React prop and never through a node, so a strip at the boundary costs nothing measured and closes the class rather than one entry. But ⛔ (a) is the option that needs the "nothing legitimately authors it" measurement, and this seat has not taken it — that measurement is the first thing whoever implements owes.

⚠️ Whichever way this is ruled, the pin is not optional. Today both kanban-ui rows are measured and unpinned, so the next refactor can move this behaviour in silence in either direction — which is the same failure mode that let the original defect ship.

Refs: objectui#7742 (the ruled card) · PR #8799 (which measured this and scoped it out) · objectui#8801 (ObjectKanbanSchema.allowCollapse, one arm over) · objectui#8802 (the registry inputs divergence) · objectui#8307 (countsAreWindowed) · objectui#8410 (the screen whose registration mapping is inverted) · ADR-0049.

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

    domain:specobjectui spec stream: fix lands on packages/types, schema corpus or spec pin coupling — spec laneplugin: kanbanpm:queuepriority:p2

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions