Out-of-scope finding from objectui#4075 / PR #4079. Filed per PD #10 — unassigned, ungraded. Observation-class: nothing a user hits today, both copies are correct and produce identical bags.
Fact
PR #4077 fixed the root-only predicate binding in DeclaredActionsBar (packages/app-shell/src/views/DeclaredActionsBar.tsx) by building the bag inline:
const predicateContext = useMemo(
() => ({ ...recordData, record: recordData, data: recordData }),
[record],
);
PR #4079 fixed the same fault on the four generic action renderers and, rather than repeating that expression four more times, gave the rule one name: usePredicateRecordContext, exported from @object-ui/react beside useCondition (packages/react/src/hooks/useExpression.ts). @object-ui/app-shell already depends on @object-ui/react and DeclaredActionsBar already imports useCondition from it, so the migration is an import plus a one-line substitution.
Why it is worth recording rather than fixing in that PR
Two implementations of one binding rule is the shape objectui#3367 / #3842 rule against, and this family has already paid for it once at the toPredicateInput level (#3314 — two normalizations drifted, and the same visible: predicate reached different verdicts depending on which path surfaced the action). Nothing has drifted here yet; the risk is that the next change to the rule lands on one copy.
One non-cosmetic difference already exists, and it is the reason this is a real migration rather than a rename. usePredicateRecordContext returns an empty bag when there is no row, instead of { record: {}, data: {} } — because useCondition merges the local context OVER the ambient predicate scope, so binding an empty record blanks out a record a host put in the scope itself (a legitimate pattern, and how action-group-dropdown-visible.test.tsx drives those leaves; it went red on the first cut of #4079). DeclaredActionsBar's inline copy does not make that distinction. It is unreachable there today — the bar is always mounted over a record — so this is not a defect report, but the migration must keep the helper's semantics rather than restore the inline ones.
Suggested disposition
Migrate DeclaredActionsBar onto usePredicateRecordContext and delete the inline useMemo, keeping the helper's no-row semantics. Small, no user-visible change, no changeset.
Dedup
Searched open issues for DeclaredActionsBar predicate binding record, usePredicateRecordContext, three ways binding duplicate. Only objectui#4075 (this finding's parent, being closed by #4079) matches; no issue covers the duplicate implementation.
Refs: objectui#4075, objectui#4077, PR #4079, objectui#3367, objectui#3842, objectui#3314.
Out-of-scope finding from objectui#4075 / PR #4079. Filed per PD #10 — unassigned, ungraded. Observation-class: nothing a user hits today, both copies are correct and produce identical bags.
Fact
PR #4077 fixed the root-only predicate binding in
DeclaredActionsBar(packages/app-shell/src/views/DeclaredActionsBar.tsx) by building the bag inline:PR #4079 fixed the same fault on the four generic action renderers and, rather than repeating that expression four more times, gave the rule one name:
usePredicateRecordContext, exported from@object-ui/reactbesideuseCondition(packages/react/src/hooks/useExpression.ts).@object-ui/app-shellalready depends on@object-ui/reactandDeclaredActionsBaralready importsuseConditionfrom it, so the migration is an import plus a one-line substitution.Why it is worth recording rather than fixing in that PR
Two implementations of one binding rule is the shape objectui#3367 / #3842 rule against, and this family has already paid for it once at the
toPredicateInputlevel (#3314 — two normalizations drifted, and the samevisible:predicate reached different verdicts depending on which path surfaced the action). Nothing has drifted here yet; the risk is that the next change to the rule lands on one copy.One non-cosmetic difference already exists, and it is the reason this is a real migration rather than a rename.
usePredicateRecordContextreturns an empty bag when there is no row, instead of{ record: {}, data: {} }— becauseuseConditionmerges the local context OVER the ambient predicate scope, so binding an empty record blanks out arecorda host put in the scope itself (a legitimate pattern, and howaction-group-dropdown-visible.test.tsxdrives those leaves; it went red on the first cut of #4079).DeclaredActionsBar's inline copy does not make that distinction. It is unreachable there today — the bar is always mounted over a record — so this is not a defect report, but the migration must keep the helper's semantics rather than restore the inline ones.Suggested disposition
Migrate
DeclaredActionsBarontousePredicateRecordContextand delete the inlineuseMemo, keeping the helper's no-row semantics. Small, no user-visible change, no changeset.Dedup
Searched open issues for
DeclaredActionsBar predicate binding record,usePredicateRecordContext,three ways binding duplicate. Only objectui#4075 (this finding's parent, being closed by #4079) matches; no issue covers the duplicate implementation.Refs: objectui#4075, objectui#4077, PR #4079, objectui#3367, objectui#3842, objectui#3314.