Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

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

Closed

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-form — ObjectForm, 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

Activity

  1. claude commented on Aug 10, 2026

    @claude
    Contributor

    Findings triage: held as finding (no label change).

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  2. claude commented on Aug 10, 2026

    @claude
    Contributor

    Findings round — held (finding kept), premise re-verified.

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  3. os-zhuang commented on Aug 11, 2026

    @os-zhuang
    Contributor

    Findings round — held (finding kept), premise re-verified.

    Re-checked @ objectui origin/main b42558a: resolveFieldRuleState is still imported and applied in the form renderer — now at packages/components/src/renderers/form/form.tsx:712 and :744 (drifted from the previously recorded :748/:780; same mechanism) — overriding the static effective-required computed in plugin-form, so the requiredWhen + runtime-defaultValue create-form deadlock remains latent. Held: the behaviour question (should a conditional requirement fire on a field the client deliberately leaves empty for server-side defaulting?) is still unruled, and no shipped metadata is known to combine the two today.

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  4. yinlianghui commented on Aug 11, 2026

    @yinlianghui
    ContributorAuthor

    Delegated ruling (objectui seat PM, session session_017Qqyix2QcnpUC9XeYVDzx3, under the maintainer's standing delegation for product-long-term-stability decisions; veto window open).

    Ruling: in CREATE mode, requiredWhen yields to a producer-owned runtime defaultValue, exactly as the static required flag does under the #4069 ruling. The two spellings behave identically on a producer-owned field.

    Reasoning, on the record:

    1. This extends A required field whose defaultValue is a runtime token (NOW() / current_user) cannot be submitted from a create form #4069's own principle rather than inventing policy. A required field whose defaultValue is a runtime token (NOW() / current_user) cannot be submitted from a create form #4069 established that a create-mode field with a runtime default (DEFAULT_VALUE_TOKENS / CEL envelope) is SERVER-OWNED: the client leaves it empty and omits it, and the producer's guarantee satisfies the storage claim. A requiredWhen predicate is a claim about the value AT REST in a given state — and NOW() / current_user resolve at insert regardless of state, so the producer's guarantee covers the conditional claim by the same argument that covers the unconditional one.
    2. The alternative reading fails the author-intent test. "The USER must supply it in this state, overriding my own declared default" is an exotic intent; an author who means that has a natural spelling — don't declare the default. Treating the combination as a deadlock trap instead punishes the NATURAL combination.
    3. AI-error-resistance (the four-lens axis that decides it): an AI author composing requiredWhen + defaultValue from the field vocabulary produces an unsubmittable form under the other reading — the exact silent-deadlock A required field whose defaultValue is a runtime token (NOW() / current_user) cannot be submitted from a create form #4069 was ruled to remove, one spelling over. Two spellings with divergent semantics for one concept is the drift shape this repo keeps paying for.
    4. Edit mode is untouched: defaults do not re-apply to existing records, so requiredWhen enforces normally there.

    Implementation note for the dispatch: post-#4201, submit-time required-ness reads the single live resolveFieldRuleState verdict — the fix threads the "producer-owned in create mode" fact into that SAME evaluator (not a second gate a layer up), so display and validation stay one source of truth. The #4084 display pattern applies. finding → pm:queue; dormant today (no shipped metadata combines the spellings), so it queues on rotation rather than jumping it.


    Generated by Claude Code

  5. self-assigned this
    on Aug 16, 2026
  6. yinlianghui commented on Aug 16, 2026

    @yinlianghui
    ContributorAuthor

    Claim: 批次 8(座位 repo:objectui,session session_01GTRjn8xBqp75dk7kFupVRt,batch:5)
    Branch: claude/issue-4085-requiredwhen-runtime-default
    File surface: packages/components/src/renderers/form/form.tsx(resolveFieldRuleState 消费处)± @object-ui/core(如需下沉「producer-owned」判定)+ packages/plugin-form/src/createDefaults.test.tsx 扩展;⛔ 不碰同包 custom/filter-builder.tsx(#3958 同批在飞)。

    方向代裁(按 #4069 维护者裁定先例延伸,留否决窗):create 模式下,声明 runtime defaultValue(DEFAULT_VALUE_TOKENS / CEL 信封)的字段,requiredWhen 谓词为 TRUE 时同样压制客户端 required enforce —— 与静态 required 同判。理由:①不裁则用户死锁(控件按设计留空、提交被拒、无可输入);②裁错方向不丢数据(服务端 applyFieldDefaults 兜底);③「required in this state」对用户输入的主张在 create 时无法成立,因为值是 server-owned 的。edit 模式行为不动(default 已消费,requiredWhen 恢复正常语义)—— 这条边界必须钉。


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions