ProtectedDefaultThe default answer: look for a foreign key on the subject entity that points at the scope entity, and compare the subject's value for that field against the scope record's ID.
Returns null when the subject has no foreign key to the scope entity at all — the
relationship is indirect and only the owning app knows how to traverse it, so declining is
more honest than answering false. Returns null for ambiguity too: when several distinct
foreign keys point at the scope entity, picking one arbitrarily would silently bind the
workflow to the wrong relationship (a Deal with both OwnerCompanyID and ClientCompanyID
has no single defensible "the" Company).
ProtectedFindNames of the subject entity's fields that are foreign keys into the scope entity.
Answers whether subject falls under the scope record identified by
scopeEntityID / scopeRecordID.
true / false for a decision, or null to decline and let the
caller fall back to the default foreign-key walk.
ProtectedValuesCompares a subject's foreign-key value against a scope record ID. Scope record IDs are stored
as text and may be UUIDs (which differ in case between SQL Server and PostgreSQL) or numeric
keys, so UUIDsEqual handles the former and a trimmed comparison the latter.
============================================================================ SCOPE RESOLUTION SEAM
An
EntityActionmay be narrowed to ONE configuration record — this Deal Type, this Contract Type, this Pipeline, this Company — via theScopeEntityID/ScopeRecordIDpair.NULLmeans "applies to every record of the entity", which is the pre-existing behaviour and by far the common case.The framework stores and filters on the pair; it does not interpret it. How a scope record relates to a subject record is an application concern: Sales knows a Deal reaches its Deal Type through
Deal.DealTypeID, and core must not. So the question "does this subject record fall under this scope record?" is answered by a@RegisterClass-resolved resolver, keyed by scope entity name, most-specific wins — the same shape asBasePriceResolverin BizApps Orders andGLAccountResolverin Accounting.The default implementation below walks the subject entity's foreign keys looking for one that points at the scope entity. That covers the direct case (Sales registers nothing for
Deal TypesbecauseDeal.DealTypeIDis found automatically). An app needing something indirect — scope by Company where the subject reaches Company through a Pipeline — registers its own resolver:Declining returns
null, notfalse.falsemeans "I looked and this record is out of scope";nullmeans "not my call" and falls back to the default. Conflating them would silently disable every binding a partially applicable resolver didn't recognise.