Skip to content

requiredWhen + a runtime defaultValue still deadlocks a create form (the conditional half of #4069) #4085

Description

@yinlianghui

Found while implementing #4069 (PR #4084). Filed unassigned; deliberately out of
that card's scope, which the maintainer's ruling framed in terms of the static
required flag.

What #4084 fixed, and where it stops

#4069 ruled that in create mode a field declaring a runtime defaultValue
(a DEFAULT_VALUE_TOKENS member — 'NOW()' / 'current_user' — or a CEL
Expression envelope) suppresses the client-side required rule and is omitted
from the payload: the control is deliberately left empty so
ObjectQL.applyFieldDefaults resolves the declaration at insert, so the field
is not "missing", it is server-owned.

PR #4084 implements that on the static flag. It resolves the effective
required when a runtime form field is built from the object schema, in
packages/plugin-formObjectForm, sectionFields (serving Modal / Drawer /
Tabbed / Split / Wizard) and the two no-sections paths.

The conditional spelling is untouched:

remind_at: Field.datetime({
  requiredWhen: 'status == "scheduled"',
  defaultValue: 'NOW()',
}),

requiredWhen is resolved downstream, in the form renderer
(packages/components/src/renderers/form/form.tsx), by resolveFieldRuleState
against the live record — it OVERRIDES the static flag that #4084 computes. So
when the predicate resolves TRUE on a create form, the field is required again,
the control is still empty by design, and the submit is refused with nothing
sensible for the user to type. Exactly the shape #4069 describes, one layer over.

Why it was not folded into #4084

Two reasons, both worth a separate decision rather than a silent widening:

  1. Surface. The fix would have to reach packages/components' form
    renderer, or thread a "this field is producer-owned" fact into
    resolveFieldRuleState in @object-ui/core. A required field whose defaultValue is a runtime token (NOW() / current_user) cannot be submitted from a create form #4069's card was explicitly
    scoped to packages/plugin-form.
  2. It is not obviously the same answer. A static required is an
    unconditional storage claim, and the producer's guarantee always covers it. A
    requiredWhen predicate says "required in this state", which an author may
    well intend as a claim about what the USER must supply in that state —
    plausibly outranking the declared default rather than yielding to it. Someone
    has to rule which reading wins; guessing risks silently disabling a rule an
    author wrote on purpose.

Not measured

No occurrence found in the shipped example apps — the same note #4069 carried
about its own combination, which had none either and was still ruled worth
fixing. Filing plainly for triage to grade rather than sitting on it; severity
judged at filing time is unreliable in both directions.

Reproduce

packages/plugin-form/src/createDefaults.test.tsx (extended in PR #4084) has the
ready-made fixture: RUNTIME_OBJECT_SCHEMA builds one required field per runtime
shape straight from the spec's DEFAULT_VALUE_TOKENS. Replace one field's
required: true with a requiredWhen that resolves TRUE and re-run the
"submits with the runtime-default fields left empty" pin.


Generated by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions