AbstractProtectedbuildBuilds the Nunjucks/template data context for the shared "Duplicate Resolution" instruction set. Both providers feed the prompt the exact same shape, so this lives on the base class. Keys match the seeded template's variable names verbatim.
ProtectedcoerceCoerces a JSON string or object into a plain record, else null.
ProtectedderiveDerive the set-level verdict from the per-candidate verdicts: Merge if any candidate is a Merge, else Uncertain if any is Uncertain, else NotDuplicate. Overall confidence is the max confidence among the candidates matching the winning recommendation (null if none reported).
ProtecteddisplayRender a field value for the prompt: the trimmed value, or '(empty)' for null/blank.
ProtectedfailedBuilds a failed reasoning output (logged, never thrown).
ProtectednormalizeNormalize the per-candidate verdicts array, dropping malformed entries. Each must carry a non-empty record id; recommendation/confidence are clamped, reasoning defaults to ''.
ProtectednormalizeClamps confidence into [0,1], returning null when absent/invalid. null (not 0) so a
missing confidence isn't mistaken for "confidently NOT a duplicate" downstream.
ProtectednormalizeNormalizes the fieldChoices array, dropping malformed entries.
ProtectednormalizeClamps the recommendation to the allowed union, defaulting to 'Uncertain'.
ProtectednormalizeReturns the survivor id string, or null when absent/empty.
ProtectedparseParses a provider's raw structured output (the {recommendation, confidence, ...}
object) into a normalized DuplicateReasoningOutput. Tolerant of either an
already-parsed object or a JSON string. Returns a failed output on any parse error.
AbstractReasonReason over one source record's matched set and return a single structured verdict.
Implementations MUST NOT throw for ordinary reasoning failures — return an output
with Success: false and an ErrorMessage instead, so one bad set never aborts a run.
the assembled matched-set context (entity, source, candidates, deltas)
the request-scoped provider + context user
Abstract reasoning provider. Subclasses implement Reason for their runtime.
Register a subclass with
@RegisterClass(DuplicateReasoningProvider, '<Mode>')where<Mode>matches the Entity Document'sReasoningMode. When no registration matches a mode,MJGlobal.ClassFactory.CreateInstancefalls back to the base class — so callers should treat a base-class instance (whose Reason throws) as "no provider for this mode" and skip reasoning gracefully.