Skip to content

schema.userActions is a sixth grid key read but undeclared — #5091's evidence claimed it was already in GRID_QUERY_INPUTS; it is not #5240

Description

@os-support-ai

Found while implementing #5091 (the per-key ruling on four (schema as any) keys). Filed unassigned, not claiming.

The correction

#5091's body offered bulkActionDefs and userActions as the two cast-read keys that are already declared, and rested its "these four are just an omission" reading on that pair. Measured on origin/main @ 118419214:

keyin GRID_QUERY_INPUTS?in ObjectGridSchema (packages/types/src/objectql.ts)?ComponentPropsMap['object-grid'] (spec 17.0.0)?
bulkActionDefs✅ yes✅ yes (:874)✅ accepted
userActionsno❌ norejected by name (unrecognized_keys)

So only bulkActionDefs was ever the symmetry. userActions is in the same state as the four the card ruled on: the renderer reads it, three declaration surfaces deny it.

Reproduce:

grep -n "userActions" packages/plugin-grid/src/index.tsx # no hit — not in GRID_QUERY_INPUTS
grep -n "userActions" packages/plugin-grid/src/ObjectGrid.tsx # :864, :1066 (plus prose)
node --input-type=module -e "import {ComponentPropsMap} from '@objectstack/spec/ui';
console.log(ComponentPropsMap['object-grid'].safeParse({objectName:'a',userActions:{}}).success)" # false

The two read sites are NOT the same channel — that is the whole question

// packages/plugin-grid/src/ObjectGrid.tsx:864 — the OBJECT's block
userActions: (objectSchema as any)?.userActions,
// packages/plugin-grid/src/ObjectGrid.tsx:1066 — a VIEW-level override, first
userActions: (schema as any).userActions ?? (resolvedSchema as any)?.userActions,

:864 feeds resolveRowCrudAffordances and is documented throughout rowCrudAffordances.ts as the object'suserActions.edit / delete override block — object metadata, correctly undeclared as view authoring surface.

:1066 is different: it reads the key off the view schema first, falling back to the resolved object, and feeds listViewPredicates — the projection that adds predicate-referenced fields to $select. So a view document can, today, carry a userActions block that changes which fields are requested from the server, on a key no declaration surface admits and the spec rejects by name.

Why this needs the same per-key ruling the four just got

Three readings, and picking one is a contract decision, not an implementation detail:

  1. Non-author surface, deliberately unlisted (what :864 clearly is) — then :1066's schema-first read is the odd one out and should read the object block only, since a view-level override of an object's CRUD affordances is a permissions-shaped decision.
  2. Author surface — then it needs the upstream spec key first: ObjectGridPropsSchema is a strictObject, so declaring it in GRID_QUERY_INPUTS alone would publish a key the save gate refuses (measured in ObjectGrid 用 (schema as any) 读的 4 个键不在 GRID_QUERY_INPUTS 里 —— #4648 要消除的「渲染器读得到、声明面否认」在这些键上仍然存在 #5091's implementation; apps/console/src/__tests__/registry-inputs-spec-parity.test.ts fails on exactly this).
  3. Dead override — if nothing writes schema.userActions, ADR-0049 enforce-or-remove applies to the read at :1066. I did not find a producer: app-shell/src/views/ObjectView.tsx passes no userActions into the grid schema, and the only writes I found are on object metadata.

Scope note

Out of scope for #5091, whose maintainer ruling of 2026-08-18 enumerates exactly four keys. Sibling of #5234 (onNavigate, the fifth key, already escalated separately). Same family as #5097 / #5102 one and two packages over.


Generated by Claude Code

Metadata

Metadata

Assignees

Labels

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions