"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.LifecycleRegistry = void 0; exports.classifyLifecycleDefect = classifyLifecycleDefect; exports.resolveLifecycle = resolveLifecycle; exports.enrichProblemUnitLifecycle = enrichProblemUnitLifecycle; exports.rankLifecycleProblemUnits = rankLifecycleProblemUnits; const stage3Lifecycle_1 = require("../types/stage3Lifecycle"); function clampUnitScore(value) { if (!Number.isFinite(value)) { return 0; } if (value <= 0) return 0; if (value >= 1) return 1; return Number(value.toFixed(2)); } function lifecycleConfidenceGrade(score) { if (score >= 0.75) return "high"; if (score >= 0.45) return "medium"; return "low"; } function uniqueStrings(values, limit = 16) { return Array.from(new Set(values.map((item) => item.trim()).filter(Boolean))).slice(0, limit); } function includesAny(source, patterns) { return patterns.some((pattern) => pattern.test(source)); } function hasToken(values, pattern) { return values.some((value) => pattern.test(value)); } function normalizeStateToken(value) { return value.trim().toLowerCase(); } function resolveStateCode(model, stateCode) { if (!stateCode || typeof stateCode !== "string") { return null; } const normalized = normalizeStateToken(stateCode); const matched = model.states.find((state) => normalizeStateToken(state.state_code) === normalized); return matched?.state_code ?? null; } function defaultInitialState(model) { const initial = model.states.find((state) => state.state_class === "initial"); if (initial) { return initial.state_code; } return model.states[0]?.state_code ?? "unknown_state"; } function defaultExpectedState(model) { const terminal = model.states.find((state) => state.is_terminal || state.state_class === "terminal"); if (terminal) { return terminal.state_code; } const active = model.states.find((state) => state.state_class === "active"); if (active) { return active.state_code; } return defaultInitialState(model); } function expectedTransitionAdjacency(model) { const graph = new Map(); for (const transition of model.transitions) { if (transition.transition_type !== "expected") { continue; } const from = transition.from_state; const to = transition.to_state; const current = graph.get(from) ?? []; if (!current.includes(to)) { current.push(to); } graph.set(from, current); } return graph; } function shortestExpectedPath(model, fromState, toState) { if (fromState === toState) { return [fromState]; } const graph = expectedTransitionAdjacency(model); const queue = [[fromState]]; const visited = new Set([fromState]); while (queue.length > 0) { const path = queue.shift(); if (!path) { continue; } const tail = path[path.length - 1]; const nextStates = graph.get(tail) ?? []; for (const nextState of nextStates) { if (visited.has(nextState)) { continue; } const nextPath = [...path, nextState]; if (nextState === toState) { return nextPath; } visited.add(nextState); queue.push(nextPath); } } return null; } function transitionEdgeLabel(fromState, toState) { return `${fromState}->${toState}`; } function resolvePreviousStates(model, currentState) { const initialState = defaultInitialState(model); if (initialState === currentState) { return []; } const path = shortestExpectedPath(model, initialState, currentState); if (!path || path.length <= 1) { return []; } return path.slice(0, -1); } const LIFECYCLE_DOMAIN_MODELS = { bank_settlement: { schema_version: stage3Lifecycle_1.LIFECYCLE_MODEL_SCHEMA_VERSION, lifecycle_domain: "bank_settlement", lifecycle_object_types: ["payment_settlement_link"], states: [ { state_code: "initiated_payment", state_label: "Платеж инициирован", state_class: "initial", entry_conditions: ["payment_order_created"], exit_conditions: ["bank_recorded"], is_terminal: false, is_problematic: false, business_meaning: "Есть инициирование платежа." }, { state_code: "bank_recorded", state_label: "Платеж отражен банком", state_class: "active", entry_conditions: ["bank_statement_recorded"], exit_conditions: ["settlement_linked"], is_terminal: false, is_problematic: false, business_meaning: "Движение денег зафиксировано, ожидается расчетное закрытие." }, { state_code: "settlement_closed", state_label: "Расчет закрыт", state_class: "terminal", entry_conditions: ["payment_to_settlement_linked"], exit_conditions: [], is_terminal: true, is_problematic: false, business_meaning: "Платеж доведен до расчетного результата." }, { state_code: "stale_unlinked_payment", state_label: "Платеж завис без закрытия", state_class: "problematic", entry_conditions: ["bank_recorded", "missing_link"], exit_conditions: ["settlement_closed"], is_terminal: false, is_problematic: true, business_meaning: "Платеж отражен, но ожидаемая связь по расчету не завершена." }, { state_code: "misclosed_payment", state_label: "Платеж закрыт некорректно", state_class: "problematic", entry_conditions: ["wrong_document_type_or_posting_mismatch"], exit_conditions: ["settlement_closed"], is_terminal: false, is_problematic: true, business_meaning: "Формальное закрытие есть, но путь закрытия неверный." } ], transitions: [ { from_state: "initiated_payment", to_state: "bank_recorded", transition_type: "expected", required_evidence: ["bank_statement_recorded"], optional_evidence: ["payment_order"], forbidden_conditions: [], business_meaning: "Платеж должен появиться во выписке." }, { from_state: "bank_recorded", to_state: "settlement_closed", transition_type: "expected", required_evidence: ["payment_to_settlement_link"], optional_evidence: ["document_to_posting"], forbidden_conditions: ["wrong_document_type"], business_meaning: "После выписки должен закрываться расчет." } ], defects: [] }, customer_settlement: { schema_version: stage3Lifecycle_1.LIFECYCLE_MODEL_SCHEMA_VERSION, lifecycle_domain: "customer_settlement", lifecycle_object_types: ["receivable_chain"], states: [ { state_code: "invoice_issued", state_label: "Реализация отражена", state_class: "initial", entry_conditions: ["realization_document_exists"], exit_conditions: ["payment_recorded"], is_terminal: false, is_problematic: false, business_meaning: "Возникла дебиторская позиция." }, { state_code: "payment_recorded", state_label: "Оплата отражена", state_class: "active", entry_conditions: ["payment_document_exists"], exit_conditions: ["receivable_closed"], is_terminal: false, is_problematic: false, business_meaning: "Оплата есть, ожидается корректное закрытие." }, { state_code: "receivable_closed", state_label: "Дебиторка закрыта", state_class: "terminal", entry_conditions: ["closing_document_linked"], exit_conditions: [], is_terminal: true, is_problematic: false, business_meaning: "Дебиторская позиция закрыта корректно." }, { state_code: "stale_receivable", state_label: "Дебиторка зависла", state_class: "problematic", entry_conditions: ["unresolved_settlement"], exit_conditions: ["receivable_closed"], is_terminal: false, is_problematic: true, business_meaning: "Позиция остается незавершенной дольше ожидаемого." } ], transitions: [ { from_state: "invoice_issued", to_state: "payment_recorded", transition_type: "expected", required_evidence: ["payment_document_exists"], optional_evidence: [], forbidden_conditions: [], business_meaning: "После реализации ожидается оплата/зачет." }, { from_state: "payment_recorded", to_state: "receivable_closed", transition_type: "expected", required_evidence: ["closing_document_linked"], optional_evidence: ["register_movement_exists"], forbidden_conditions: ["cross_branch_inconsistency"], business_meaning: "Оплата должна завершаться корректным закрытием расчета." } ], defects: [] }, deferred_expense: { schema_version: stage3Lifecycle_1.LIFECYCLE_MODEL_SCHEMA_VERSION, lifecycle_domain: "deferred_expense", lifecycle_object_types: ["deferred_expense_item"], states: [ { state_code: "recognized", state_label: "РБП признан", state_class: "initial", entry_conditions: ["deferred_expense_created"], exit_conditions: ["writeoff_started"], is_terminal: false, is_problematic: false, business_meaning: "РБП поставлен на учет." }, { state_code: "partially_written_off", state_label: "Частичное списание", state_class: "active", entry_conditions: ["partial_writeoff_exists"], exit_conditions: ["fully_written_off"], is_terminal: false, is_problematic: false, business_meaning: "Списание идет по графику." }, { state_code: "fully_written_off", state_label: "РБП полностью списан", state_class: "terminal", entry_conditions: ["full_writeoff_exists"], exit_conditions: [], is_terminal: true, is_problematic: false, business_meaning: "РБП завершил lifecycle." }, { state_code: "overdue_writeoff", state_label: "Просроченное списание", state_class: "problematic", entry_conditions: ["period_boundary", "missing_link"], exit_conditions: ["fully_written_off"], is_terminal: false, is_problematic: true, business_meaning: "РБП живет дольше допустимого окна." } ], transitions: [], defects: [] }, fixed_asset: { schema_version: stage3Lifecycle_1.LIFECYCLE_MODEL_SCHEMA_VERSION, lifecycle_domain: "fixed_asset", lifecycle_object_types: ["fixed_asset_card"], states: [ { state_code: "capitalized", state_label: "Капвложения отражены", state_class: "initial", entry_conditions: ["capitalization_document_exists"], exit_conditions: ["accepted_for_accounting"], is_terminal: false, is_problematic: false, business_meaning: "Объект зафиксирован как вложение." }, { state_code: "accepted_for_accounting", state_label: "Принят к учету", state_class: "active", entry_conditions: ["acceptance_document_exists"], exit_conditions: ["depreciation_active"], is_terminal: false, is_problematic: false, business_meaning: "Объект переведен в основной контур учета." }, { state_code: "depreciation_active", state_label: "Амортизация активна", state_class: "active", entry_conditions: ["depreciation_register_movement"], exit_conditions: ["disposed"], is_terminal: false, is_problematic: false, business_meaning: "Жизненный цикл ОС идет штатно." }, { state_code: "contradictory_asset_state", state_label: "Противоречивый статус ОС", state_class: "problematic", entry_conditions: ["posting_mismatch_or_wrong_path"], exit_conditions: ["depreciation_active"], is_terminal: false, is_problematic: true, business_meaning: "Статус ОС формально есть, но смыслово противоречив." }, { state_code: "disposed", state_label: "Выбыл", state_class: "terminal", entry_conditions: ["disposal_document_exists"], exit_conditions: [], is_terminal: true, is_problematic: false, business_meaning: "Жизненный цикл ОС завершен." } ], transitions: [], defects: [] }, vat_flow: { schema_version: stage3Lifecycle_1.LIFECYCLE_MODEL_SCHEMA_VERSION, lifecycle_domain: "vat_flow", lifecycle_object_types: ["vat_document_chain"], states: [ { state_code: "vat_registered", state_label: "НДС отражен документно", state_class: "initial", entry_conditions: ["invoice_registered"], exit_conditions: ["vat_reflected"], is_terminal: false, is_problematic: false, business_meaning: "Сформирован первичный документный слой НДС." }, { state_code: "vat_reflected", state_label: "НДС отражен в учете", state_class: "active", entry_conditions: ["vat_register_movement"], exit_conditions: ["vat_deducted"], is_terminal: false, is_problematic: false, business_meaning: "НДС проходит штатную стадию отражения." }, { state_code: "vat_deducted", state_label: "НДС принят к вычету", state_class: "terminal", entry_conditions: ["deduction_confirmed"], exit_conditions: [], is_terminal: true, is_problematic: false, business_meaning: "НДС-цепочка завершена корректно." }, { state_code: "vat_conflict", state_label: "Конфликт НДС-цепочки", state_class: "problematic", entry_conditions: ["cross_branch_inconsistency"], exit_conditions: ["vat_reflected"], is_terminal: false, is_problematic: true, business_meaning: "Бухгалтерская и налоговая ветки расходятся." } ], transitions: [], defects: [] }, period_close: { schema_version: stage3Lifecycle_1.LIFECYCLE_MODEL_SCHEMA_VERSION, lifecycle_domain: "period_close", lifecycle_object_types: ["period_close_blocker"], states: [ { state_code: "preclose_checks", state_label: "Предзакрытие", state_class: "active", entry_conditions: ["period_scope_detected"], exit_conditions: ["close_ready"], is_terminal: false, is_problematic: false, business_meaning: "Идет проверка готовности периода." }, { state_code: "close_ready", state_label: "Готов к закрытию", state_class: "active", entry_conditions: ["no_blockers_detected"], exit_conditions: ["close_completed"], is_terminal: false, is_problematic: false, business_meaning: "Период может быть закрыт." }, { state_code: "close_completed", state_label: "Закрытие завершено", state_class: "terminal", entry_conditions: ["close_operation_done"], exit_conditions: [], is_terminal: true, is_problematic: false, business_meaning: "Период закрыт." }, { state_code: "close_blocked", state_label: "Закрытие заблокировано", state_class: "problematic", entry_conditions: ["period_close_risk_or_stale_state"], exit_conditions: ["close_ready"], is_terminal: false, is_problematic: true, business_meaning: "Есть lifecycle-дефекты, влияющие на закрытие." }, { state_code: "close_contradicted", state_label: "Закрыт формально, но с противоречием", state_class: "problematic", entry_conditions: ["misclosed_or_cross_branch_conflict"], exit_conditions: ["close_completed"], is_terminal: false, is_problematic: true, business_meaning: "Формальное закрытие не согласовано с фактическими ветками." } ], transitions: [], defects: [] } }; const SHARED_DEFECTS = [ { defect_code: "missing_expected_transition", defect_class: "path", severity_hint: "medium", business_meaning: "Ожидаемый переход не произошел.", evidence_requirements: ["expected_state", "missing_transition_signal"], period_impact_potential: "indirect" }, { defect_code: "invalid_transition", defect_class: "path", severity_hint: "high", business_meaning: "Переход произошел по некорректному пути.", evidence_requirements: ["invalid_transition_signal"], period_impact_potential: "indirect" }, { defect_code: "stale_active_state", defect_class: "timing", severity_hint: "high", business_meaning: "Объект завис в активном состоянии.", evidence_requirements: ["stale_marker", "missing_transition_signal"], period_impact_potential: "direct" }, { defect_code: "contradictory_state", defect_class: "consistency", severity_hint: "high", business_meaning: "Статусы объекта противоречат друг другу.", evidence_requirements: ["contradiction_signal"], period_impact_potential: "direct" }, { defect_code: "premature_terminal_state", defect_class: "closure", severity_hint: "medium", business_meaning: "Терминальное состояние наступило преждевременно.", evidence_requirements: ["terminal_state", "missing_required_previous_state"], period_impact_potential: "indirect" }, { defect_code: "misclosed_state", defect_class: "closure", severity_hint: "high", business_meaning: "Контур формально закрыт, но закрыт неверно.", evidence_requirements: ["wrong_closure_path"], period_impact_potential: "direct" }, { defect_code: "orphan_intermediate_state", defect_class: "path", severity_hint: "medium", business_meaning: "Промежуточная стадия осталась без корректного продолжения.", evidence_requirements: ["intermediate_state_without_next"], period_impact_potential: "indirect" }, { defect_code: "cross_branch_state_conflict", defect_class: "consistency", severity_hint: "high", business_meaning: "Состояния соседних веток учета противоречат друг другу.", evidence_requirements: ["cross_branch_conflict_signal"], period_impact_potential: "direct" } ]; for (const domain of stage3Lifecycle_1.STAGE3_LIFECYCLE_DOMAINS) { LIFECYCLE_DOMAIN_MODELS[domain].defects = SHARED_DEFECTS; } class LifecycleRegistryImpl { models; constructor(models) { this.models = models; } listDomains() { return stage3Lifecycle_1.STAGE3_LIFECYCLE_DOMAINS.slice(); } getDomain(domain) { return this.models[domain]; } hasState(domain, stateCode) { const model = this.getDomain(domain); return Boolean(resolveStateCode(model, stateCode)); } resolveDefaultExpectedState(domain) { return defaultExpectedState(this.getDomain(domain)); } resolveInitialState(domain) { return defaultInitialState(this.getDomain(domain)); } findExpectedPath(domain, fromState, toState) { return shortestExpectedPath(this.getDomain(domain), fromState, toState); } } exports.LifecycleRegistry = new LifecycleRegistryImpl(LIFECYCLE_DOMAIN_MODELS); function inferLifecycleDomain(input) { const unitTokens = [ input.unit.problem_unit_type, input.unit.business_defect_class, input.unit.mechanism_summary, input.unit.failed_expected_edge ?? "", input.unit.expected_state ?? "", input.unit.actual_state ?? "", ...input.unit.affected_accounts, ...input.unit.affected_entities, ...input.unit.affected_documents, ...input.unit.affected_counterparties, ...input.candidates.flatMap((item) => item.anomaly_patterns), ...input.candidates.flatMap((item) => item.relation_pattern_hits) ] .join(" ") .toLowerCase(); const hasVatMarkers = includesAny(unitTokens, [ /domain_hint:vat_flow/, /\binvoice_to_vat\b/, /\bvat_chain_conflict\b/, /(^|[^a-z0-9])nds([^a-z0-9]|$)/, /(^|[^a-z0-9])vat([^a-z0-9]|$)/, /(^|[^a-z0-9])tax(?:es)?([^a-z0-9]|$)/, /\baccount[_:\s-]?(19|68)\b/ ]); const hasDeferredMarkers = includesAny(unitTokens, [ /domain_hint:deferred_expense/, /\bdeferred(?:_expense)?\b/, /\bdeferred_expense_to_writeoff\b/, /\bwriteoff\b/, /\bpartially_written_off\b/, /\bfully_written_off\b/, /\baccount[_:\s-]?97\b/ ]); const hasFixedAssetMarkers = includesAny(unitTokens, [ /domain_hint:fixed_asset/, /\bfixed[_\s-]?asset(?:s)?\b/, /\basset_card_to_depreciation\b/, /\bdepreciation(?:_active)?\b/, /\baccepted_for_accounting\b/, /\bcapitalized\b/, /\baccount[_:\s-]?(01|02|08)\b/ ]); const hasPeriodCloseMarkers = includesAny(unitTokens, [ /domain_hint:period_close/, /\bperiod[_\s-]?close\b/, /\bperiod_close_risk\b/, /\bclose[_\s-]?risk\b/, /\bclosure[_\s-]?risk\b/, /\bpreclose\b/, /\bmonth[_\s-]?close\b/, /\bperiod_risk\b/ ]); if (hasDeferredMarkers) { return "deferred_expense"; } if (hasFixedAssetMarkers) { return "fixed_asset"; } if (hasVatMarkers) { return "vat_flow"; } if (hasPeriodCloseMarkers || input.unit.problem_unit_type === "period_risk_cluster" || input.unit.period_impact?.impact_class === "close_risk") { return "period_close"; } if (includesAny(unitTokens, [/buyer/, /customer/, /\b62\b/])) { return "customer_settlement"; } if (includesAny(unitTokens, [ /domain_hint:bank_settlement/, /\bpayment_to_settlement\b/, /\bstatement_to_document\b/, /\bbank_recorded\b/, /\binitiated_payment\b/, /\bsettlement(?:_closed)?\b/ ]) || input.unit.problem_unit_type === "unresolved_settlement_cluster" || input.unit.problem_unit_type === "broken_chain_segment") { return "bank_settlement"; } if (input.unit.problem_unit_type === "cross_branch_inconsistency_cluster") { return "vat_flow"; } if (input.unit.problem_unit_type === "lifecycle_anomaly_node") { return "deferred_expense"; } return "bank_settlement"; } function inferCurrentState(domain, input) { const anomalies = input.candidates.flatMap((item) => item.anomaly_patterns).map((item) => item.toLowerCase()); const relations = input.candidates.flatMap((item) => item.relation_pattern_hits).map((item) => item.toLowerCase()); const hasStale = hasToken(anomalies, /(no_continuation|stale|tail|missing_link|broken_lifecycle|partially_linked)/); const hasInvalid = hasToken(anomalies, /(posting_mismatch|wrong_document_type|cross_domain_inconsistency|misclose|cross_branch)/); if (domain === "bank_settlement") { if (hasInvalid) return "misclosed_payment"; if (hasStale) return "stale_unlinked_payment"; if (hasToken(relations, /payment_to_settlement/)) return "bank_recorded"; return "initiated_payment"; } if (domain === "customer_settlement") { if (hasStale) return "stale_receivable"; if (hasToken(relations, /payment|settlement/)) return "payment_recorded"; return "invoice_issued"; } if (domain === "deferred_expense") { if (hasStale) return "overdue_writeoff"; if (hasToken(relations, /writeoff|partial/)) return "partially_written_off"; return "recognized"; } if (domain === "fixed_asset") { if (hasInvalid) return "contradictory_asset_state"; if (hasToken(relations, /depreciation|amort/)) return "depreciation_active"; if (hasToken(relations, /accept|account/)) return "accepted_for_accounting"; return "capitalized"; } if (domain === "vat_flow") { if (hasInvalid || hasToken(anomalies, /cross_branch|inconsistency/)) return "vat_conflict"; if (hasToken(relations, /invoice_to_vat|vat/)) return "vat_reflected"; return "vat_registered"; } if (hasInvalid) return "close_contradicted"; if (hasStale || input.unit.period_impact?.impact_class === "close_risk") return "close_blocked"; return "preclose_checks"; } function inferExpectedState(domain, input, model) { const explicitExpected = input.unit.expected_state?.trim(); if (explicitExpected) { return explicitExpected; } return defaultExpectedState(model); } function inferMissingTransition(input, model, currentState, expectedState) { if (typeof input.unit.failed_expected_edge === "string" && input.unit.failed_expected_edge.trim().length > 0) { return input.unit.failed_expected_edge.trim(); } const anomalies = input.candidates.flatMap((item) => item.anomaly_patterns).join(" ").toLowerCase(); if (!/(missing_link|no_continuation|broken_lifecycle|tail|unresolved)/.test(anomalies)) { return null; } if (currentState !== expectedState) { const path = shortestExpectedPath(model, currentState, expectedState); if (path && path.length >= 2) { return transitionEdgeLabel(path[0], path[1]); } } const directExpected = model.transitions.find((transition) => transition.transition_type === "expected" && transition.from_state === currentState); if (directExpected) { return transitionEdgeLabel(directExpected.from_state, directExpected.to_state); } return "expected_transition_not_observed"; } function inferInvalidTransition(input, model) { const anomalies = input.candidates.flatMap((item) => item.anomaly_patterns).join(" ").toLowerCase(); for (const transition of model.transitions) { for (const forbiddenCondition of transition.forbidden_conditions) { if (anomalies.includes(forbiddenCondition.toLowerCase())) { return `${transitionEdgeLabel(transition.from_state, transition.to_state)}:forbidden:${forbiddenCondition}`; } } } if (/(cross_branch|cross_domain_inconsistency)/.test(anomalies)) { return "cross_branch_conflict_transition"; } if (/(wrong_document_type|posting_mismatch|misclose)/.test(anomalies)) { return "invalid_document_or_posting_transition"; } return null; } function classifyLifecycleDefect(input) { const current = input.currentState.toLowerCase(); if (input.invalidTransition?.includes("cross_branch")) { return "cross_branch_state_conflict"; } if (input.invalidTransition) { if (current.includes("misclosed") || input.domain === "period_close") { return "misclosed_state"; } return "invalid_transition"; } if (input.missingTransition) { if (current.includes("stale") || current.includes("overdue") || input.periodCloseSensitive) { return "stale_active_state"; } return "missing_expected_transition"; } if (current.includes("contradict")) { return "contradictory_state"; } if (current.includes("closed") && !input.expectedState.toLowerCase().includes("closed")) { return "premature_terminal_state"; } if (input.currentState !== input.expectedState && !input.currentState.toLowerCase().includes("closed")) { return "orphan_intermediate_state"; } return null; } function registryBackedDefect(domain, defect) { if (!defect) { return null; } const model = exports.LifecycleRegistry.getDomain(domain); return model.defects.some((definition) => definition.defect_code === defect) ? defect : null; } function resolutionConfidence(unitConfidence, input) { let score = unitConfidence.score; if (input.hasExplicitStates) score += 0.1; if (input.hasDefectSignal) score += 0.08; if (input.candidateCount >= 2) score += 0.05; if (input.hasSnapshotLimitations) score -= 0.12; const normalized = clampUnitScore(score); return { score: normalized, grade: lifecycleConfidenceGrade(normalized) }; } function staleDurationHint(domain, defect, input) { const anomalies = input.candidates.flatMap((item) => item.anomaly_patterns).join(" ").toLowerCase(); if (defect !== "stale_active_state") { return undefined; } if (/(period_boundary|period|close_risk)/.test(anomalies) || domain === "period_close") { return "period_boundary_exceeded"; } return "unknown_snapshot_window"; } function lifecycleInterpretation(input) { const base = `Текущая стадия: ${input.currentState}; ожидаемая стадия: ${input.expectedState}.`; if (input.defect === "stale_active_state") { return `${base} Объект завис во времени и не дошел до ожидаемого перехода.`; } if (input.defect === "misclosed_state") { return `${base} Контур закрыт формально, но путь закрытия противоречит бухгалтерской логике.`; } if (input.defect === "cross_branch_state_conflict") { return `${base} Между ветками домена ${input.domain} обнаружено противоречие состояний.`; } if (input.defect === "missing_expected_transition") { return `${base} Не зафиксирован ожидаемый переход (${input.missingTransition ?? "unknown_transition"}).`; } if (input.defect === "invalid_transition") { return `${base} Зафиксирован некорректный переход (${input.invalidTransition ?? "invalid_transition"}).`; } return `${base} Lifecycle-разрешение не выявило критичный дефект, но состояние требует наблюдения.`; } function resolveLifecycle(input) { const lifecycle_domain = inferLifecycleDomain(input); const model = exports.LifecycleRegistry.getDomain(lifecycle_domain); const inferredCurrentState = inferCurrentState(lifecycle_domain, input); const inferredExpectedState = inferExpectedState(lifecycle_domain, input, model); const explicitActualState = input.unit.actual_state?.trim() ?? null; const explicitExpectedState = input.unit.expected_state?.trim() ?? null; const explicitCurrentState = resolveStateCode(model, explicitActualState); const explicitExpectedResolved = resolveStateCode(model, explicitExpectedState); const inferredCurrentResolved = resolveStateCode(model, inferredCurrentState); const inferredExpectedResolved = resolveStateCode(model, inferredExpectedState); const currentState = explicitCurrentState ?? inferredCurrentResolved ?? defaultInitialState(model); const expectedState = explicitExpectedResolved ?? inferredExpectedResolved ?? defaultExpectedState(model); const missingTransition = inferMissingTransition(input, model, currentState, expectedState); const invalidTransition = inferInvalidTransition(input, model); const detectedDefect = classifyLifecycleDefect({ domain: lifecycle_domain, currentState, expectedState, missingTransition, invalidTransition, periodCloseSensitive: input.unit.period_impact?.impact_class === "close_risk" }); const defect = registryBackedDefect(lifecycle_domain, detectedDefect); const evidenceIds = uniqueStrings(input.unit.evidence_pack, 8); const previousStates = resolvePreviousStates(model, currentState); const limitations = uniqueStrings([ ...input.unit.snapshot_limitations, ...(input.candidates.some((item) => item.confidence_hint === "low") ? ["low_confidence_candidates_present"] : []), ...(explicitActualState && !explicitCurrentState ? ["actual_state_not_in_registry_normalized"] : []), ...(explicitExpectedState && !explicitExpectedResolved ? ["expected_state_not_in_registry_normalized"] : []), ...(explicitCurrentState ? [] : ["actual_state_inferred"]), ...(explicitExpectedResolved ? [] : ["expected_state_inferred"]) ], 8); const confidence = resolutionConfidence(input.unit.confidence, { hasExplicitStates: Boolean(explicitCurrentState || explicitExpectedResolved), hasDefectSignal: Boolean(defect || missingTransition || invalidTransition), candidateCount: input.candidates.length, hasSnapshotLimitations: limitations.length > 0 }); return { lifecycle_object_id: `lcobj-${input.unit.problem_unit_id}`, lifecycle_domain, resolved_current_state: currentState, resolved_expected_state: expectedState, resolved_previous_states: previousStates, missing_transitions: missingTransition ? [missingTransition] : [], invalid_transitions: invalidTransition ? [invalidTransition] : [], detected_defects: defect ? [defect] : [], state_confidence: confidence, resolution_evidence: evidenceIds, snapshot_limitations: limitations }; } function lifecycleRanking(defect, input) { let score = input.unit.severity.score; const basis = ["base_problem_severity"]; if (defect === "cross_branch_state_conflict") { score += 0.55; basis.push("cross_branch_conflict_weight"); } else if (defect === "misclosed_state") { score += 0.45; basis.push("misclosed_state_weight"); } else if (defect === "stale_active_state") { score += 0.35; basis.push("stale_duration_weight"); } else if (defect === "invalid_transition") { score += 0.3; basis.push("invalid_transition_weight"); } else if (defect === "missing_expected_transition") { score += 0.25; basis.push("missing_transition_weight"); } if (input.staleDuration) { score += 0.15; basis.push("stale_duration_present"); } if (input.unit.period_impact?.impact_class === "close_risk") { score += 0.22; basis.push("period_close_impact"); } if (input.resolution.state_confidence.grade === "high") { score += 0.08; basis.push("state_confidence_weight"); } return { lifecycle_ranking_score: Number(score.toFixed(2)), lifecycle_ranking_basis: basis }; } function enrichProblemUnitLifecycle(input) { const resolution = resolveLifecycle(input); const defect = resolution.detected_defects[0] ?? null; const staleDuration = staleDurationHint(resolution.lifecycle_domain, defect, input); const ranking = lifecycleRanking(defect, { unit: input.unit, resolution, staleDuration }); return { ...input.unit, lifecycle_domain: resolution.lifecycle_domain, lifecycle_object_id: resolution.lifecycle_object_id, current_lifecycle_state: resolution.resolved_current_state, expected_lifecycle_state: resolution.resolved_expected_state, ...(resolution.missing_transitions.length > 0 ? { missing_transition: resolution.missing_transitions[0] } : {}), ...(resolution.invalid_transitions.length > 0 ? { invalid_transition: resolution.invalid_transitions[0] } : {}), ...(defect ? { lifecycle_defect_type: defect } : {}), ...(staleDuration ? { stale_duration: staleDuration } : {}), lifecycle_confidence: resolution.state_confidence, business_lifecycle_interpretation: lifecycleInterpretation({ domain: resolution.lifecycle_domain, currentState: resolution.resolved_current_state, expectedState: resolution.resolved_expected_state, defect, missingTransition: resolution.missing_transitions[0] ?? null, invalidTransition: resolution.invalid_transitions[0] ?? null }), lifecycle_resolution: resolution, lifecycle_ranking_score: ranking.lifecycle_ranking_score, lifecycle_ranking_basis: ranking.lifecycle_ranking_basis }; } function rankLifecycleProblemUnits(units) { return units .slice() .sort((left, right) => { const rankDiff = (right.lifecycle_ranking_score ?? 0) - (left.lifecycle_ranking_score ?? 0); if (rankDiff !== 0) return rankDiff; const severityDiff = right.severity.score - left.severity.score; if (severityDiff !== 0) return severityDiff; return right.confidence.score - left.confidence.score; }); }