Skip to content

Console FormPage still drops the other three conditional-rule surfaces after #5594: section-level visibleWhen/visibleOn, and the object-level field rules visibleWhen/readonlyWhen/requiredWhen #5627

Description

@os-sales

Found while implementing #5594 (wiring the view-level field visibility predicate into
apps/console/src/components/FormPage.tsx). Filed unassigned for PM triage — outside that
card's scope: its triage fence declared it "the predicate wiring plus a regression pin",
and the two surfaces below are different keys with different user-visible consequences, so
folding them in would have been unreviewable scope creep rather than a bounded fix.

Verified on 8c87f0583 (the merge-base of the #5594 branch), before and after that change.

Mechanism

FormPage.tsx is the second, independent form renderer — its own buildSections, its own
JSX, serving both /f/:slug (public, anonymous) and /forms/:name (internal). After
#5594 it honours one of the four conditional-rule surfaces the sibling chain honours.
The other three are still dropped, and both drops happen in the same place: the two local
types that describe the payloads, which admit none of these keys, so nothing downstream can
read them.

1. Section-level visibleWhen / visibleOn

The local section type declares four keys and no visibility at all:

interfaceFormSectionSpec{label?: string;collapsible?: boolean;collapsed?: boolean;columns?: 1|2|3|4|'1'|'2'|'3'|'4';fields: Array[string|FormFieldSpec];// angle brackets spelled out; see note below}

and the render maps every section unconditionally (FormPage.tsx:1398):

{sections.map((sec, i) => (

The app-shell counterpart does declare and honour it — FormSectionSpec in
packages/app-shell/src/views/metadata-admin/SchemaForm.tsx carries
visibleWhen/visibleOn (documented there as "Canonical section visibility predicate
(ADR-0089)"), and SchemaFormBody filters sections through readVisibility before
rendering. So a section an author conditioned away renders in full on the two console form
routes, including the public one.

2. Object-level field rules visibleWhen / readonlyWhen / requiredWhen

The local object-schema field type admits eight keys and none of the ADR-0036 rules:

interfaceObjectFieldDef{type: string;label?: string;required?: boolean;defaultValue?: unknown;maxLength?: number;options?: ...;placeholder?: string;helpText?: string;}

buildSections reads only those, so RenderableField.readonly is override.readonly ?? false
and required is override.required ?? def.required ?? false — static, never conditional.

The sibling chain carries all three out of the object schema and resolves them through
resolveFieldRuleStatepackages/plugin-form/src/sectionFields.ts, fromObjectSchema:

// Field-level conditional rules (ADR-0036) — the form renderer resolves// them via `resolveFieldRuleState`; without this copy a sectioned form// silently dropped rules that the flat (schema-order) form honors.visibleWhen: field.visibleWhen,readonlyWhen: field.readonlyWhen,requiredWhen: field.requiredWhen,

Note that #5594's fix reads the view-level predicate off the FormField override only.
An object-level visibleWhen — authored on the object's field, not on the form view — is a
separate slot the sibling keeps separate on purpose (sectionFields.ts routes the view
predicate into visibleOn precisely so the object rule is never clobbered), and this
renderer reads neither of those two.

Why this is not a duplicate

Searched open issues before filing (section visibility / FormSectionSpec / readonlyWhen
/ requiredWhen / console form renderer / conditional field phrasings). #5596 is the only
open match and it is about the type declarations, not the evaluation.

Severity note

User-reachable on the public /f/:slug route, same as #5594 — and the object-level half is
worse than fail-open hiding: a readonlyWhen that should lock a field leaves it editable,
which pairs badly with the server's fail-CLOSED behaviour for the unbound-scope case
(objectstack#4889, described in packages/core/src/evaluator/fieldRules.ts) — the user
edits, the save reports success, and the value never lands.

Two questions a fix has to answer rather than assume, which is part of why this is filed
instead of folded in:

  1. Whether a section hidden by its predicate should also suppress its fields' values on
    submit — Console FormPage (the standalone /forms and internal FormView renderer) never evaluates visibleWhen/visibleOn — objectui#2212 was fixed in the OTHER form renderer #5594 deliberately kept conditional visibility a rendering rule for fields
    (a hidden field still submits its value, matching the plugin-form chain), and the same
    question repeats at section granularity.
  2. Whether requiredWhen here needs the serverOwnedValue carve-out resolveFieldRuleState
    applies (A required field whose defaultValue is a runtime token (NOW() / current_user) cannot be submitted from a create form #4069 / requiredWhen + a runtime defaultValue still deadlocks a create form (the conditional half of #4069) #4085), given this route serves both create and edit (type: 'form' action fired from a record opens an empty CREATE form — /forms/:name ignores the ?recordId= ActionRunner forwards #4278).

Note on the code blocks above

The two type excerpts have their generic angle brackets spelled out as Array[...]
GitHub's body sanitizer eats short angle-bracket fragments, fenced code included (this was
measured on the #5594 PR body, which had Record of string to unknown silently reduced
to Record). Read the real declarations at apps/console/src/components/FormPage.tsx.


Generated by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

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

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions