Skip to content

Two authored-predicate evaluators still pass undefined scope after #6010 — console FormPage (both routes) and the wizard's submit-time required re-check #6110

Description

@claude

Filed unassigned by the objectstack domain:devx dev seat while implementing the documentation half of objectui#6010 (objectstack#11740). Recording only — not graded, no domain:*.

Measured on objectui origin/main @ a100f77, by content.

What #6010 fixed, and where it did not reach

objectui#6010 (PR #6037) bound the host predicateScope on the five AUTHORED-predicate call sites in packages/components/src/renderers/form/form.tsx, so current_user (plus the ADR-0068 user / ctx.user / os.user aliases) resolves on form field visibleWhen / visibleOn the same way it already did on the page/app-nav node gate and per-option visibleWhen. Verified at form.tsx:1229, :1265, :1272, :1990, :2003.

Two other AUTHORED-predicate evaluators in this repo still pass undefined for that argument, so the defect #6010 closed on one chain is still live on them:

1. apps/console/src/components/FormPage.tsx — the SECOND form renderer, on both of its routes.

apps/console/src/components/FormPage.tsx:610 evalFieldPredicate(field.visibleWhen, values, true, previous ?? undefined, undefined, …)
apps/console/src/components/FormPage.tsx:651 evalFieldPredicate(section.visibleWhen, values, true, previous ?? undefined, undefined, …)

That file's own header names its surfaces: the authed internal form route /forms/:name (GET /api/v1/meta/view/:name + POST /api/v1/data/:object) and the public anonymous form /f/:slug. The internal route is a runtime record surface by ADR-0089 D1's own words ("runtime record surfaces bind record + current_user"), and its visibleWhen metadata is the same *.view.ts FormView the object-view chain renders. So a role gate authored once behaves differently depending on which route opens the form — which is the asymmetry #6010's parity pin exists to refuse, one renderer over.

Fail direction is the dangerous one, unchanged from #6010: evalFieldPredicate is called with fallback: true, so the unbound root faults OPEN and the field/section a current_user test was meant to hide is shown to everyone.

(The public /f/:slug route has no authenticated principal, so "unbound" is arguably correct there — but it is the same call site, so the two routes cannot currently be told apart. Deciding what an anonymous form binds is part of this card, not a reason to skip it.)

2. packages/plugin-form/src/WizardForm.tsx:440 / :471 — the submit-time required-field re-check.

packages/plugin-form/src/WizardForm.tsx:440 resolveFieldRuleState({ visibleWhen, readonlyWhen, requiredWhen }, record, { … }, undefined, undefined, …)
packages/plugin-form/src/WizardForm.tsx:471 evalFieldPredicate((field as any).visibleOn, record, true, undefined, undefined, …)

This is not a render path — it is missingRequiredByStep, the gate that re-checks the WHOLE declared field set at final submit because allowSkip can jump past a step. Its docstring states the intent verbatim: "the same one the form renderer and the server's rule-validator use, so a conditionally required/hidden field gets the same verdict from all three rather than a second, divergent dialect." Since #6010 the form renderer's verdict binds current_user and this gate's does not, so a field the wizard HIDES from this user (predicate false with the scope bound) is still counted as visible here (predicate faults open with no scope) and, if required, blocks the submit on a field the submitter cannot see or fill in.

Not covered by this card

Why it is filed here rather than fixed in the docs PR

Found while correcting objectstack's visibleWhen binding docs (objectstack#11740). The docs there now state the binding per surface, and cite this card for the surfaces that are still unbound — so this card closing is what lets that text be simplified.


Generated by Claude Code


Generated by Claude Code

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpm:dispatched

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions