Skip to content

The disabled / disabledOn node gate has NO fault diagnostic in either build — and its fail-soft polarity greys a control out rather than showing it #6445

Description

@claude

Found while censusing predicate-fault catch sites for #6038. Filed unassigned; out of that card's scope, which the 2026-08-25 ruling scopes to a visibleWhen / node-gate visibility predicate.

The site

packages/react/src/SchemaRenderer.tsx, immediately below the visibility chain:

constisDisabled=(()=>{if(hasDeclaredPredicate(newSchema.disabled)){returnevaluator.evaluateCondition(newSchema.disabled);}if(hasDeclaredPredicate(newSchema.disabledOn)){returnevaluator.evaluateCondition(newSchema.disabledOn);}returnfalse;})();

The visibility chain three lines above routes every leg through evaluateVisibilityPredicate, which reports a fault (in development since #5454, and in production since #6038). This one calls evaluateCondition bare — so a faulting disabled predicate has never been reported, in either build, in any dialect that does not report on its own.

Why the polarity makes it worth its own card

evaluateCondition's answer for an unevaluable predicate is true. On the negated visibility legs that means SHOWN; on disabled it means GREYED OUT. This file already carries the analysis for the neighbouring case (#3862/#3955): an empty disabled predicate disabling the node was a real defect, and the comment there records the asymmetry — "a greyed-out control is still on screen, a node that never rendered is indistinguishable from metadata that meant it."

So the failure mode here is a control the user can see and cannot use, with no console line naming the predicate. That is a support ticket ("the Save button is greyed out and I don't know why") with nothing to grep for.

Cost

The expensive part of this question was settled by #6038: EvaluationOptions.onFault reports the fault the evaluator has already caught, at the same number of engine calls — no throwOnError double-evaluation — and the reporter, message and rate limit already exist. The remaining work is the wiring plus a decision on the message text, since reportUnresolvableVisibilityPredicate's copy is written about a visibility gate ("the gate did NOT bite") and is wrong for this polarity: here the safe default is the one that bites.

Related: #6038 (the ruling, the seam, the reporter), #3862 and #3955 (the disabled/hidden declared-predicate rulings this polarity analysis comes from).


Generated by Claude Code

Metadata

Metadata

Assignees

Labels

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

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions