Этап 4 добивка двух хвостов: settlement polarity и чистое разделение month_close / RBP в live acceptance
This commit is contained in:
@@ -26,6 +26,8 @@ export interface TemporalWindow {
|
||||
export interface ClaimBoundAnchorAudit {
|
||||
claim_type: ClaimType;
|
||||
settlement_role?: "supplier" | "customer" | "mixed" | "unknown";
|
||||
settlement_role_resolution_reason?: string[];
|
||||
polarity_resolution_status?: "resolved_supplier" | "resolved_customer" | "mixed" | "unknown" | "not_applicable";
|
||||
required_anchors: string[];
|
||||
resolved_anchors: Record<string, string[]>;
|
||||
missing_anchors: string[];
|
||||
@@ -431,12 +433,29 @@ export function resolveClaimBoundAnchors(input: {
|
||||
if (!allowedContextWindow && input.primaryPeriod) {
|
||||
reasonCodes.push("controlled_temporal_expansion_window_unavailable");
|
||||
}
|
||||
const settlementRole = resolveSettlementRole({
|
||||
const settlementRoleRaw = resolveSettlementRole({
|
||||
claimType,
|
||||
counterpartyScope: resolvedAnchors.counterparty_scope ?? [],
|
||||
accountPrefixes,
|
||||
userMessage: input.userMessage
|
||||
});
|
||||
const settlementRole = typeof settlementRoleRaw === "string" ? settlementRoleRaw : undefined;
|
||||
const settlementRoleReason: string[] =
|
||||
claimType === "prove_settlement_closure_state" || claimType === "prove_advance_offset_state"
|
||||
? settlementRole
|
||||
? [`settlement_role_resolved_${settlementRole}`]
|
||||
: ["no_supplier_customer_anchor"]
|
||||
: [];
|
||||
const polarityResolutionStatus =
|
||||
claimType === "prove_settlement_closure_state" || claimType === "prove_advance_offset_state"
|
||||
? settlementRole === "supplier"
|
||||
? "resolved_supplier"
|
||||
: settlementRole === "customer"
|
||||
? "resolved_customer"
|
||||
: settlementRole === "mixed"
|
||||
? "mixed"
|
||||
: "unknown"
|
||||
: "not_applicable";
|
||||
if (
|
||||
(claimType === "prove_settlement_closure_state" || claimType === "prove_advance_offset_state") &&
|
||||
(settlementRole === "mixed" || settlementRole === "unknown")
|
||||
@@ -447,6 +466,8 @@ export function resolveClaimBoundAnchors(input: {
|
||||
return {
|
||||
claim_type: claimType,
|
||||
settlement_role: settlementRole,
|
||||
settlement_role_resolution_reason: settlementRoleReason,
|
||||
polarity_resolution_status: polarityResolutionStatus,
|
||||
required_anchors: requiredAnchors,
|
||||
resolved_anchors: resolvedAnchors,
|
||||
missing_anchors: missingAnchors,
|
||||
|
||||
@@ -848,12 +848,25 @@ export function resolveDomainPolarityGuard(input: {
|
||||
(/(?:сч[её]т\s*62|по\s*62|счет\s*62|РїРѕ\s*62)/i.test(lower) ? 1 : 0);
|
||||
|
||||
let polarity: DomainPolarity = "mixed_or_unresolved";
|
||||
if (supplierScore > 0 && customerScore === 0) {
|
||||
polarity = "supplier_payable";
|
||||
} else if (customerScore > 0 && supplierScore === 0) {
|
||||
polarity = "customer_receivable";
|
||||
if (supplierScore > 0 || customerScore > 0) {
|
||||
if (supplierScore >= customerScore + 2) {
|
||||
polarity = "supplier_payable";
|
||||
} else if (customerScore >= supplierScore + 2) {
|
||||
polarity = "customer_receivable";
|
||||
} else if (prefixes.has("60") && !prefixes.has("62")) {
|
||||
polarity = "supplier_payable";
|
||||
} else if (prefixes.has("62") && !prefixes.has("60")) {
|
||||
polarity = "customer_receivable";
|
||||
}
|
||||
}
|
||||
const unresolved = polarity === "mixed_or_unresolved";
|
||||
const reasonCodes = unresolved ? ["unresolved_supplier_customer_polarity"] : [];
|
||||
if (unresolved && supplierScore > 0 && customerScore > 0) {
|
||||
reasonCodes.push("supplier_customer_signals_conflict");
|
||||
}
|
||||
if (unresolved && supplierScore === 0 && customerScore === 0) {
|
||||
reasonCodes.push("supplier_customer_signals_absent");
|
||||
}
|
||||
return {
|
||||
applied: true,
|
||||
polarity,
|
||||
@@ -868,7 +881,7 @@ export function resolveDomainPolarityGuard(input: {
|
||||
rejected_problem_units: 0,
|
||||
rejected_evidence: 0,
|
||||
critical_contradiction: unresolved,
|
||||
reason_codes: unresolved ? ["unresolved_supplier_customer_polarity"] : []
|
||||
reason_codes: uniqueStrings(reasonCodes)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -202,18 +202,45 @@ function hasP0ClaimSignal(claimType, focusDomainHint) {
|
||||
focusDomainHint === "month_close_costs_20_44" ||
|
||||
focusDomainHint === "fixed_asset_amortization");
|
||||
}
|
||||
function hasSettlementScopeSignal(input) {
|
||||
const claim = String(input.claimType ?? "").trim();
|
||||
const domain = String(input.focusDomainHint ?? "").trim();
|
||||
if (claim === "prove_settlement_closure_state" || claim === "prove_advance_offset_state" || domain === "settlements_60_62") {
|
||||
return true;
|
||||
}
|
||||
if (Boolean(input.followupApplied) && domain === "settlements_60_62") {
|
||||
return true;
|
||||
}
|
||||
const lower = String(input.userMessage ?? "").toLowerCase();
|
||||
if (/(?:60(?:\\.\\d{2})?|62(?:\\.\\d{2})?|76(?:\\.\\d{2})?|оплат|расч[её]т|зач[её]т|аванс|долг|хвост|supplier|customer|settlement|payable|receivable|поставщ|покупат)/i.test(lower)) {
|
||||
return true;
|
||||
}
|
||||
const accounts = Array.isArray(input.companyAnchors?.accounts) ? input.companyAnchors.accounts : [];
|
||||
return accounts.some((item) => /^(?:51|60|62|76)(?:\\.|$)/.test(String(item ?? "").trim()));
|
||||
}
|
||||
function resolveBusinessScopeFromLiveContext(input) {
|
||||
const current = input.current;
|
||||
const routeSummary = current?.route_summary_resolved;
|
||||
const julyResolved = isJuly2020TemporalResolved(input.temporalGuard);
|
||||
const p0Signal = hasP0ClaimSignal(input.claimType, input.focusDomainHint);
|
||||
if (!julyResolved || !p0Signal) {
|
||||
const settlementScopeSignal = hasSettlementScopeSignal({
|
||||
userMessage: input.userMessage,
|
||||
companyAnchors: input.companyAnchors,
|
||||
claimType: input.claimType,
|
||||
focusDomainHint: input.focusDomainHint,
|
||||
followupApplied: input.followupApplied
|
||||
});
|
||||
const shouldRecoverScope = p0Signal && (julyResolved || settlementScopeSignal);
|
||||
if (!shouldRecoverScope) {
|
||||
return current;
|
||||
}
|
||||
const reasons = Array.isArray(current.scope_resolution_reason) ? [...current.scope_resolution_reason] : [];
|
||||
if (!reasons.includes("temporal_claim_bound_company_scope_recovery")) {
|
||||
if (julyResolved && !reasons.includes("temporal_claim_bound_company_scope_recovery")) {
|
||||
reasons.push("temporal_claim_bound_company_scope_recovery");
|
||||
}
|
||||
if (settlementScopeSignal && !reasons.includes("settlement_claim_company_scope_recovery")) {
|
||||
reasons.push("settlement_claim_company_scope_recovery");
|
||||
}
|
||||
const currentScopes = Array.isArray(current.business_scope_resolved) ? current.business_scope_resolved : [];
|
||||
let changed = false;
|
||||
const normalizedScopes = currentScopes
|
||||
@@ -1747,7 +1774,10 @@ export class AssistantService {
|
||||
current: initialBusinessScopeResolution,
|
||||
temporalGuard,
|
||||
claimType: claimAnchorAudit.claim_type,
|
||||
focusDomainHint: focusDomainForGuards
|
||||
focusDomainHint: focusDomainForGuards,
|
||||
userMessage,
|
||||
companyAnchors,
|
||||
followupApplied: Boolean(followupBinding.usage?.applied)
|
||||
});
|
||||
const resolvedRouteSummary = businessScopeResolution.route_summary_resolved;
|
||||
const requirementExtraction = extractRequirements(resolvedRouteSummary, normalized.normalized, userMessage);
|
||||
@@ -1966,6 +1996,9 @@ export class AssistantService {
|
||||
resolved_account_anchors: polarityGuardResult.audit.resolved_account_anchors,
|
||||
domain_polarity_guard: polarityGuardResult.audit,
|
||||
claim_anchor_audit: claimAnchorAudit,
|
||||
settlement_role: claimAnchorAudit.settlement_role ?? null,
|
||||
settlement_role_resolution_reason: claimAnchorAudit.settlement_role_resolution_reason ?? [],
|
||||
polarity_resolution_status: claimAnchorAudit.polarity_resolution_status ?? "not_applicable",
|
||||
targeted_evidence_acquisition: targetedEvidenceResult.audit,
|
||||
evidence_admissibility_gate: evidenceGateResult.audit,
|
||||
...(rbpLiveRouteAudit ? { rbp_live_route_audit: rbpLiveRouteAudit } : {}),
|
||||
@@ -2060,6 +2093,9 @@ export class AssistantService {
|
||||
resolved_account_anchors: polarityGuardResult.audit.resolved_account_anchors,
|
||||
domain_polarity_guard: polarityGuardResult.audit,
|
||||
claim_anchor_audit: claimAnchorAudit,
|
||||
settlement_role: claimAnchorAudit.settlement_role ?? null,
|
||||
settlement_role_resolution_reason: claimAnchorAudit.settlement_role_resolution_reason ?? [],
|
||||
polarity_resolution_status: claimAnchorAudit.polarity_resolution_status ?? "not_applicable",
|
||||
targeted_evidence_acquisition: targetedEvidenceResult.audit,
|
||||
evidence_admissibility_gate: evidenceGateResult.audit,
|
||||
...(rbpLiveRouteAudit ? { rbp_live_route_audit: rbpLiveRouteAudit } : {}),
|
||||
|
||||
@@ -120,6 +120,8 @@ export interface ClaimBoundAnchorAuditDebug {
|
||||
| "prove_rbp_tail_state"
|
||||
| "prove_fixed_asset_amortization_coverage";
|
||||
settlement_role?: "supplier" | "customer" | "mixed" | "unknown";
|
||||
settlement_role_resolution_reason?: string[];
|
||||
polarity_resolution_status?: "resolved_supplier" | "resolved_customer" | "mixed" | "unknown" | "not_applicable";
|
||||
required_anchors: string[];
|
||||
resolved_anchors: Record<string, string[]>;
|
||||
missing_anchors: string[];
|
||||
@@ -341,6 +343,9 @@ export interface AssistantDebugPayload {
|
||||
resolved_account_anchors?: string[];
|
||||
domain_polarity_guard?: DomainPolarityGuardDebug;
|
||||
claim_anchor_audit?: ClaimBoundAnchorAuditDebug;
|
||||
settlement_role?: ClaimBoundAnchorAuditDebug["settlement_role"];
|
||||
settlement_role_resolution_reason?: ClaimBoundAnchorAuditDebug["settlement_role_resolution_reason"];
|
||||
polarity_resolution_status?: ClaimBoundAnchorAuditDebug["polarity_resolution_status"];
|
||||
targeted_evidence_acquisition?: TargetedEvidenceAcquisitionDebug;
|
||||
evidence_admissibility_gate?: EvidenceAdmissibilityGateDebug;
|
||||
rbp_live_route_audit?: RbpLiveRouteAuditDebug;
|
||||
|
||||
Reference in New Issue
Block a user