Skip to content

finding(app-shell): the field-visibility filter gates on objectDef.fields[].visible, a key @objectstack/spec refuses — so the gate cannot be authored through the spec at all #6514

Description

@os-support-ai

Found while implementing objectui#6493 (binding the roots on the two ad-hoc field-visibility evaluators). Filed unassigned, recording only — not graded, no type.

Measured on origin/main @ d09e13fd7, and in the framework checkout beside it.

What was measured

RecordFormPage and AppContent both filter an object's fields with
evaluateVisibility(f.visible, expressionEvaluator) — i.e. they gate on
objectDef.fields[].visible. objectui#6493 makes that evaluator bind the roots this
tier declares; it deliberately did not ask whether the KEY it reads exists.

It does not. @objectstack/spec's FieldSchema is a strictObject, and visible
appears in its FIELD_KEY_GUIDANCE map as prose that REFUSES the spelling rather than
an alias that renames it (packages/spec/src/data/field.zod.ts):

visible is not a field key, and which key you want depends on the form: a static
boolean is hidden — INVERTED, so visible: false is hidden: true — while a
per-record CEL predicate is visibleWhen (shown only when TRUE). Its siblings are
readonlyWhen and requiredWhen.

The guidance's own comment says the prose form was chosen over a rename deliberately,
because "this surface declares BOTH forms and the two answers have opposite polarity".
The per-OPTION schema, by contrast, does alias it: SelectOptionSchema carries
aliases: { ..., visible: 'visibleWhen' }. So the field level is the one place the
spelling is answered with a refusal.

objectui's own packages/types agrees: field metadata declares visible_on and
visibleWhen; there is no visible on any field metadata interface (the one
visible?: string | { dialect, source } in objectql.ts is on BulkActionDef).

Scanned all 104 *.object.{ts,yml,json} files in the framework, tracking the enclosing
top-level section of every visible occurrence: 29 in actions, 15 in fields, and
every one of the 15 is prose, a comment, or a per-option visibleWhen — not one is a
field-level visible key. Nothing in the example apps authors one either.

Why it is worth recording

The gate is therefore unreachable through the authoring surface: a field visible
written in object metadata is rejected by the schema before it can reach these two
call sites, and nothing that ships authors one. Two consequences worth separating:

  • objectui#6493's behaviour change is latent, not live — which is what it reports.
  • The two call sites read a key the platform's own contract tells authors not to write.
    That is the question objectui#6262 flagged for its own gap and this card inherits:
    the honest repair may be to read visibleWhen (and hidden) here instead, or for the
    spec to declare visible at field level. Both are decisions, not cleanups.

The awkward half, stated plainly

If the answer is "read visibleWhen", note that the spec declares field-level
visibleWhenhas no current_user — deliberately, and it names the exact failure
mode as the intended one:

  • field.zod.ts, on the per-option predicate: "Env: the live record plus the host
    predicate scope, which binds current_user — wider than field-level visibleWhen,
    which has no current_user."
  • view.zod.ts, on FormField.visibleWhen: "No current_user at field level — it is
    unbound here and the predicate would fault open (per-option visibleWhen is the
    surface that binds it)."

ADR-0068 D1 pulls the other way for the key that is actually being evaluated today: one
user object under current_user / user / ctx.user / os.user, evaluating
identically "in a formula, an RLS policy, and a client visible gate". objectui#6493
conformed the evaluator to D1 because user was already bound there and three spellings
of the same object were not. Whether a field-level gate should see the user at all is the
question above, and it is one for the maintainer.

Related, and NOT a duplicate

  • objectui#6493 — binds the roots on these two evaluators. Does not touch which key they read.
  • objectui#6262 — /forms/:name under-feeds the provider. Different mechanism, same family of question.
  • objectui#6505 — dev-mode validateSchema types visible/disabled as boolean-only. That is
    the COMPONENT schema's gate and a type claim in a validator; this is object FIELD metadata
    and a strict-schema refusal.

Generated by Claude Code

Metadata

Metadata

Assignees

Labels

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

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions