Skip to content

Commit 8bb9dcf

Browse files
committed
Centralize SCL binding decision classification
1 parent 5ae7243 commit 8bb9dcf

1 file changed

Lines changed: 59 additions & 0 deletions

File tree

src/ProcessBus.Core/Services/Scl/BindingCandidateEligibility.cs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,63 @@ public static bool IsEligible(
3939
};
4040
}
4141

42+
/// <summary>
43+
/// Produces the final dashboard classification for an eligible binding candidate.
44+
/// The ordering intentionally mirrors the UI contract: hard conflicts dominate,
45+
/// then ambiguity, then field mismatch, followed by clean confidence outcomes.
46+
/// </summary>
47+
public static string Classify(
48+
int score,
49+
int mismatchCount,
50+
bool ambiguous,
51+
bool expectedConflict,
52+
bool liveAlreadyMatched,
53+
string? warningReason)
54+
{
55+
if (expectedConflict || liveAlreadyMatched)
56+
return "CONFLICT";
57+
58+
if (ambiguous)
59+
return "AMBIGUOUS";
60+
61+
if (mismatchCount > 0)
62+
return "MISMATCH";
63+
64+
if (score >= GenericScoreThreshold)
65+
return string.IsNullOrWhiteSpace(warningReason) ? "MATCHED" : "MATCHED_WITH_WARNING";
66+
67+
return "WEAK";
68+
}
69+
70+
/// <summary>
71+
/// Evaluates eligibility and final classification together for regression tests
72+
/// and other callers that need one deterministic decision contract.
73+
/// </summary>
74+
public static BindingCandidateDecision Evaluate(
75+
string protocol,
76+
int score,
77+
int mismatchCount,
78+
bool ambiguous,
79+
bool expectedConflict,
80+
bool liveAlreadyMatched,
81+
string? warningReason,
82+
IReadOnlyCollection<string> matchedFields)
83+
{
84+
var eligible = IsEligible(protocol, score, mismatchCount, matchedFields);
85+
if (!eligible)
86+
return new BindingCandidateDecision(false, "INELIGIBLE");
87+
88+
return new BindingCandidateDecision(
89+
true,
90+
Classify(
91+
score,
92+
mismatchCount,
93+
ambiguous,
94+
expectedConflict,
95+
liveAlreadyMatched,
96+
warningReason));
97+
}
98+
4299
private static bool HasPrimaryIdentityAnchor(
43100
string protocol,
44101
int score,
@@ -65,3 +122,5 @@ bool Has(string fieldName)
65122
return score >= GenericScoreThreshold;
66123
}
67124
}
125+
126+
public sealed record BindingCandidateDecision(bool IsEligible, string Status);

0 commit comments

Comments
 (0)