fix(react): extend the adapter-only data.* constant-predicate diagnostic to disabled / disabledOn - #6702
Merged
os-sales merged 1 commit intoAug 28, 2026
Conversation
…ostic to `disabled` / `disabledOn` `reportAdapterOnlyDataPredicate` (objectui#5687) reports a node-gate predicate that evaluates perfectly, against the wrong object: at the node tier `data` is the data-source adapter, not the row, so `data.status == 'locked'` is a constant for every row. It was wired only into the visibility chain's dev leg. The `disabled` / `disabledOn` gate was not covered: a predicate like `data.status == 'locked'` evaluates cleanly (no fault, so objectui#6445's fault reporter correctly stays silent), and on the constant's other polarity (`data.locked == null`, `!data.assignee`, or an adapter answering nothing) hands the gate a constant `true` that greys the control out on every row, in every build, with nothing on the console. Per the 2026-08-27 maintainer ruling (option A) on objectui#6504: extend the diagnostic to the enablement gate, dev-only (option C, always-on, was excluded, outside the #5687 precedent), with its own copy for the constant-`true` direction rather than reusing the visibility leg's "hides the node" sentence. Both legs carry the same objectui#5330 dissolution pointer. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_8ca04858-ea8e-5b85-9182-de59aa49e00c
Contributor
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
os-sales
marked this pull request as ready for review
August 28, 2026 17:05
os-sales
deleted the
claude/issue-6504-disabled-gate-constant-predicate
branch
August 28, 2026 17:25
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #6504
What
Extends objectui#5687's adapter-only
data.*constant-predicate diagnostic to thedisabled/disabledOngate — dev-only, per the 2026-08-27 maintainer ruling (Option A)recorded on this issue.
reportAdapterOnlyDataPredicatenames a predicate that evaluates perfectly, against thewrong object: at the node tier
datais the data-source adapter, not the row, sodata.status == 'locked'is a constant for every row. It was wired only into thevisibility chain's dev leg (
evaluateVisibilityPredicate). Adisabledpredicate writtenthe same way faults nothing (#6445's fault reporter correctly stays silent), and on the
constant's other polarity (
data.locked == null,!data.assignee, or an adapter answeringnothing) hands the gate a constant
truethat greys the control out on every row, in everybuild, with nothing on the console.
evaluateEnablementPredicatenow reports it too, indevelopment only — option C (always-on) stays excluded, outside the #5687 precedent.
The copy, and why its direction differs from the visibility leg
The visibility leg's sentence — "a constant
falsehides the node on every row whilelooking exactly like a gate that said no" — is about the wrong polarity for this gate and
would be a false statement here. The ruling named the direction explicitly: on
disabled,the dangerous constant is
true. The newenablemententry inADAPTER_ONLY_GATE_COPY(
packages/react/src/utils/visibilityDiagnostic.ts) reads:It is not a reuse of the visibility sentence — the two are pinned as distinct strings, and
one new group-4-style test in
SchemaRenderer.disabledGateFaultDiagnostic.test.tsxassertsthe enablement line does not contain
hides theand the visibility line does not containDISABLED.Where the dissolution pointer lives, and its verbatim source
Both #5687's own docblock and the new binding-inheritance clause are carried forward:
unresolvedDataPaths's "why this discriminator" section already reads (verbatim, unchangedby this PR): "a diagnostic on a card that dissolves when objectui#5330's deprecation
window closes." This PR appends a clause at that exact spot — "BOTH legs together, the
enablement one objectui#6504 added included" — and adds a new docblock on
ADAPTER_ONLY_GATE_COPYitself that quotes the 2026-08-27 ruling's binding clause verbatimand names every symbol + both call sites (
evaluateVisibilityPredicateandevaluateEnablementPredicateinSchemaRenderer.tsx) a #5330-window teardown has to deletein the same stroke. Neither leg is independently scheduled.
Per the stop condition: #5330's deprecation window is not closing near-term — measured,
not assumed. Its Phase 2 carrier (#5741, the stored-metadata survey that gates removal) is
still open,
pm:awaiting-maintainer, unassigned, blocked on a live-tenant data channel noseat currently has. Proceeding per the ruling.
Ablation — both legs predicted before running, mutation/restoration proven on disk
Leg 1 — the new leg must produce the diagnostic, with the correct copy (
evaluateEnablementPredicate's new call site removed).Predicted: every new group-6 test that asserts a report turns red; every silence/negative
control stays green. Observed: exactly as predicted — 8/8 red (the acceptance
criterion, the constant-
truedirection, the copy-is-not-reused assertion, thecross-gate content check,
disabledOn, the two dedupe cases, and the pre-existing test thisPR rewrote from a negative to a positive pin), 24/24 other cases (including every
false-positive/negative control) stayed green. Mutation confirmed on disk (call-site grep
count 1→0, blob hash changed); restore confirmed (
git diff HEADempty, hash back to57c0beb3f…).Leg 2 — false-positive direction (a genuinely row-dependent predicate must not fire it).
Mutated the shared discriminator regex (
DATA_ROOT_PATH_RE, unchanged by this PR's actualdiff) to also treat
recordas a matched root, to probe whether the shared false-positiveprotection this leg inherits (rather than reimplements) is real. Prediction missed, and
reported as observed rather than forced: I predicted the new
FALSE-POSITIVE CONTROLtest (
disabled: "record.status == 'open'") andnodeGateDataPredicate.test.tsx's3awould flip; neither did — both stay silent behind an independent guard
(
source.indexOf('data.') === -1fast-reject: neither predicate string contains theliteral substring
data.at all, so the mutated regex is never reached). What did flipwere two pre-existing, unrelated pins in
nodeGateDataPredicate.test.tsx—2d(a quoted
'data.status'literal) and3d(record.data.status, whose.beforedatathe original regex's exclusion group is specifically built to reject) — both already-hardened
false-positive guards this card didn't write, doing their job against a hostile version of
code this card also didn't write. Net: this leg adds no new discriminator logic (no new
regex, no new resolution algorithm), only a second call into the existing, shared,
already-tested function with a
gatelabel — so its false-positive resistance is byconstruction identical to the visibility leg's, not a new surface to separately harden.
Mutation confirmed on disk (marker count 0→1, hash changed); restore confirmed (
git diff HEADempty, hash back to40e466e7d…).Leg 3 — the visibility leg is unchanged.
evaluateVisibilityPredicate's call site(line ~772 in
SchemaRenderer.tsx) is untouched — nogateargument added, still defaultsto
'visibility'.SchemaRenderer.nodeGateDataPredicate.test.tsx(40 tests, the full#5687/#5756 suite) and
SchemaRenderer.concealmentGateFaultDiagnostic.test.tsxpassunmodified, byte-identical to
origin/main. Leg 1's mutation (which only touched theenablement call site) independently confirms zero effect on any visibility-leg test.
Gate table
pnpm --filter '@object-ui/react^...' buildtypes/core/data-objectstack/i18nallDonepnpm --filter '@object-ui/react' run type-check(tsc --noEmit && tsc -p tsconfig.test.json)disabledGateFaultDiagnostic.test.tsxpnpm exec vitest run packages/react/src/__tests__/SchemaRenderer.disabledGateFaultDiagnostic.test.tsxTest Files 1 passed (1)/Tests 32 passed (32)pnpm exec vitest run packages/react/src/__tests__/SchemaRenderer.nodeGateDataPredicate.test.tsx packages/react/src/__tests__/SchemaRenderer.concealmentGateFaultDiagnostic.test.tsxTest Files 2 passed (2)/Tests 40 passed (40)pnpm exec vitest run packages/react/Test Files 62 passed (62)/Tests 926 passed (926)— re-run after ablation restore, identicalcheck:control-bytesnode scripts/check-control-bytes.mjs✅ OK (scanned 5553 tracked text file(s); skipped 85 binary)node scripts/check-changeset-presence.mjs✅ 3 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)node scripts/check-changeset-no-major.mjs✅ No changeset declares a major bumppnpm exec eslint --no-inline-config --format json <3 touched files>no-explicit-anywarnings onSchemaRenderer.tsx(none on touched lines — verified against the same file's content atorigin/main's9abc02ab2via--stdin, also 0 errors/18 warnings, byte-identical count)Lint narrowing justification (repo has no type-aware linting configured — no
project/projectServiceineslint.config.js— so this diff cannot move any judgment on anuntouched file): file count is eslint's own
--format jsonoutput (3), not a guess; theconfig's rule set is untyped, so the invariance claim is structural, not assumed.
Union re-run:
20c52cedf(branch head, pushed) — the commit these results are quotedagainst.
Fence
Touched only
packages/react/src/utils/visibilityDiagnostic.ts,packages/react/src/SchemaRenderer.tsx(theevaluateEnablementPredicatecall site theexisting docblock there explicitly anticipated this card would fill in), and one test file.
Did not reach
packages/types'exportsmap,app-shell, orplugin-designer— nooverlap with #6527's in-flight patch round.
No published type widened:
AdapterOnlyPredicateGateKindandADAPTER_ONLY_ENABLEMENT_PREDICATE_PREFIXare module-internal exports (not re-exported frompackages/react/src/index.ts), matching #5687's ownADAPTER_ONLY_DATA_PREDICATE_PREFIX/formatAdapterOnlyDataMessage/reportAdapterOnlyDataPredicate, none of which are on thepackage's public barrel either. No runtime behaviour change outside
__DEV__.Generated by Claude Code