Found while implementing #5542 (converging the console's FormFieldSpec onto the
shared app-shell declaration). Filed unassigned for PM triage — outside that card's
declared file surface, which was the type declaration and its pin, not the renderer.
Mechanism
apps/console/src/components/FormPage.tsx is a second, independent form renderer:
it has its own buildSections and its own JSX, and it serves both the public
/forms/:slug route and the internal /forms/:name route. Verified on the merged ref
cad512fe1, it contains zero occurrences of visibleWhen or visibleOn:
grep -c 'visibleWhen\|visibleOn' apps/console/src/components/FormPage.tsx -> 0
The only visibility it honours is the static boolean, at :1300:
{sec.fields.filter((f) => !f.hidden).map((f) => (
So a FormView field carrying a conditional predicate — legal, spec-strict metadata that
@objectstack/spec normalises to visibleWhen (ADR-0089), and that the metadata-admin
designer both authors and honours — renders unconditionally on these two routes.
Fail-open and silent: an author who conditions a field on record.priority == 'urgent'
sees it always, with no diagnostic.
Why this is not a duplicate of #2212
#2212 recorded exactly this symptom and PR #2214 fixed it — in a different chain:
ModalForm → resolveFormViewLayout → packages/plugin-formsectionFields.ts →
packages/componentsrenderers/form/form.tsx. FormPage.tsx is not on that chain at
any point; it never calls buildSectionFields and never renders through
SchemaRenderer type:'form'. The fix landed in one of the two renderers that read this
key, and the fact that there were two was not visible from inside either.
That is the same reason #5542 exists one level down: one contract, several independent
implementations, each only ever checked against itself. #2212's own regression test
lives with the chain it fixed, so nothing in the suite can see this renderer's copy.
Severity note
User-reachable, unlike #5542 — this is a rendered-page behaviour on the public form
route, not a typing gap. But it is a fail-open, not a data-integrity defect, and the
correct fix direction is #2212's ruling verbatim: route the predicate through the
canonical engine (evalFieldPredicate, packages/coreevaluator/fieldRules.ts),
which already accepts string and the dialect/source object arm. Whether that is
worth doing here or whether FormPage.tsx should instead be folded onto the plugin-form
chain is an architectural call, not mine at filing time — the second renderer is arguably
the real finding.
Boundary
Found while implementing #5542 (converging the console's
FormFieldSpeconto theshared app-shell declaration). Filed unassigned for PM triage — outside that card's
declared file surface, which was the type declaration and its pin, not the renderer.
Mechanism
apps/console/src/components/FormPage.tsxis a second, independent form renderer:it has its own
buildSectionsand its own JSX, and it serves both the public/forms/:slugroute and the internal/forms/:nameroute. Verified on the merged refcad512fe1, it contains zero occurrences ofvisibleWhenorvisibleOn:The only visibility it honours is the static boolean, at
:1300:So a FormView field carrying a conditional predicate — legal, spec-strict metadata that
@objectstack/specnormalises tovisibleWhen(ADR-0089), and that the metadata-admindesigner both authors and honours — renders unconditionally on these two routes.
Fail-open and silent: an author who conditions a field on
record.priority == 'urgent'sees it always, with no diagnostic.
Why this is not a duplicate of #2212
#2212 recorded exactly this symptom and PR #2214 fixed it — in a different chain:
ModalForm→resolveFormViewLayout→packages/plugin-formsectionFields.ts→packages/componentsrenderers/form/form.tsx.FormPage.tsxis not on that chain atany point; it never calls
buildSectionFieldsand never renders throughSchemaRenderer type:'form'. The fix landed in one of the two renderers that read thiskey, and the fact that there were two was not visible from inside either.
That is the same reason #5542 exists one level down: one contract, several independent
implementations, each only ever checked against itself. #2212's own regression test
lives with the chain it fixed, so nothing in the suite can see this renderer's copy.
Severity note
User-reachable, unlike #5542 — this is a rendered-page behaviour on the public form
route, not a typing gap. But it is a fail-open, not a data-integrity defect, and the
correct fix direction is #2212's ruling verbatim: route the predicate through the
canonical engine (
evalFieldPredicate,packages/coreevaluator/fieldRules.ts),which already accepts
stringand thedialect/sourceobject arm. Whether that isworth doing here or whether
FormPage.tsxshould instead be folded onto the plugin-formchain is an architectural call, not mine at filing time — the second renderer is arguably
the real finding.
Boundary
conditional field phrasings). Form-view FormField.visibleOn (CEL) is never evaluated — conditional fields always render #2212, Conditional-visibility predicates (
visibleWhen/visibleOn) fail OPEN and silently — a broken predicate is indistinguishable from no predicate #4051, DrawerForm's no-sections field builder dropsvisibleWhen/readonlyWhen/requiredWhen(andgroup) — ModalForm's identical builder carries them #4755 and Renderer/actionvisible/disabledpredicates bypass the canonical CEL engine — home-grown JS evaluator diverges from server enforcement #2661 all match the class; allare closed, and none of them touches
apps/console/src/components/FormPage.tsx.it. [finding] A THIRD inline copy of the form-field authoring contract lives in apps/console FormPage.tsx — objectui#5040 converged only the app-shell two #5542 only made the key declarable in this file — the console's local type did
not admit
visibleWhenat all, so the gap was previously invisible from the type sidetoo.