Post-F: закрепить metadata scope и обновить live7 canary

This commit is contained in:
dctouch 2026-04-24 19:11:46 +03:00
parent 796a9a93f2
commit 7262603a26
8 changed files with 687 additions and 53 deletions

View File

@ -168,6 +168,7 @@ Materially hardened in this pass:
- explicit raw entity search after a ranking/value-flow dialogue now resets stale assistant/followup scope: `Найди в 1С Группу СВК` becomes `source_signal=raw_text` with `explicit_entity_candidates=["Группа СВК"]`, no stale organization/date/ranking scope in the discovery turn input, and downstream follow-ups then carry the grounded SVK object intentionally; - explicit raw entity search after a ranking/value-flow dialogue now resets stale assistant/followup scope: `Найди в 1С Группу СВК` becomes `source_signal=raw_text` with `explicit_entity_candidates=["Группа СВК"]`, no stale organization/date/ranking scope in the discovery turn input, and downstream follow-ups then carry the grounded SVK object intentionally;
- open-organization ranked value-flow questions now stay subjectless by design: predicate-shaped entity pollution such as `принёс наибольшую выручку организации в` is discarded, referential organization placeholders such as `эта организация` are resolved through the active organization scope, and the MCP ranking answer overrides exact-lane year-summary replies when the user asked `кто`; - open-organization ranked value-flow questions now stay subjectless by design: predicate-shaped entity pollution such as `принёс наибольшую выручку организации в` is discarded, referential organization placeholders such as `эта организация` are resolved through the active organization scope, and the MCP ranking answer overrides exact-lane year-summary replies when the user asked `кто`;
- ranked value-flow answer drafts no longer leak raw English/internal evidence lines into the user-facing response; - ranked value-flow answer drafts no longer leak raw English/internal evidence lines into the user-facing response;
- metadata-scoped movement/document chains now keep organization scope and metadata subject separate: stale follow-up `counterparty` values that are really the active organization, or action-verb pollution such as `Провести`, no longer become `explicit_entity_candidates` during year switches and document pivots;
- VAT exact materialization now survives period-window filtering instead of collapsing into `recipe_visibility_gap`; - VAT exact materialization now survives period-window filtering instead of collapsing into `recipe_visibility_gap`;
- confirmed VAT tax-period turns now keep raw month extraction separate from runtime tax-quarter execution, so exact VAT routes do not lose the intended tax period; - confirmed VAT tax-period turns now keep raw month extraction separate from runtime tax-quarter execution, so exact VAT routes do not lose the intended tax period;
- post-pivot receivables recovery now preserves the selected open-items recipe without weakening strict account-scope guards for risk replies; - post-pivot receivables recovery now preserves the selected open-items recipe without weakening strict account-scope guards for risk replies;
@ -188,7 +189,7 @@ Replay-backed anchors for the current layer include:
- `address_truth_harness_phase80_payments_to_contracts_to_documents_all_time_live_rerun1` - `address_truth_harness_phase80_payments_to_contracts_to_documents_all_time_live_rerun1`
- `address_truth_harness_phase82_human_mixed_integrity_status_dialog_post_m23_rerun_documents_scope_bidirectional` - `address_truth_harness_phase82_human_mixed_integrity_status_dialog_post_m23_rerun_documents_scope_bidirectional`
- `address_truth_harness_phase82_human_mixed_integrity_status_dialog_post_f_account_injection_guard_clean_scope` - `address_truth_harness_phase82_human_mixed_integrity_status_dialog_post_f_account_injection_guard_clean_scope`
- `address_truth_harness_post_f_cross_stage_canary_agent_20260424_live5`, accepted `24/24`, and saved into autoruns as `AGENT | Post-F cross-stage semantic integrity canary` (`gen-ag04241406-abe4d8`) - `address_truth_harness_post_f_cross_stage_canary_agent_20260424_live7`, accepted `24/24`, and saved into autoruns as `AGENT | Post-F cross-stage semantic integrity canary` (`gen-ag04241406-abe4d8`)
## Honest Remaining Risk ## Honest Remaining Risk

View File

@ -65,7 +65,7 @@ Current honest status:
- pre-multidomain readiness: `~88%` - pre-multidomain readiness: `~88%`
- bounded-autonomy foundation readiness: `~86%` - bounded-autonomy foundation readiness: `~86%`
- open-world bounded-autonomy readiness: `~71%` - open-world bounded-autonomy readiness: `~71%`
- graph snapshot after latest rebuild: `5891 nodes`, `12769 edges`, `135 communities` - graph snapshot after latest rebuild: `5892 nodes`, `12772 edges`, `137 communities`
- current breakpoint: - current breakpoint:
- the validated hot paths are no longer structurally broken; - the validated hot paths are no longer structurally broken;
- flagship continuity collapse is no longer the primary risk; - flagship continuity collapse is no longer the primary risk;
@ -92,7 +92,7 @@ Latest live proof now includes:
- `address_truth_harness_phase80_payments_to_contracts_to_documents_all_time_live_rerun1` accepted - `address_truth_harness_phase80_payments_to_contracts_to_documents_all_time_live_rerun1` accepted
- `address_truth_harness_phase82_human_mixed_integrity_status_dialog_post_m23_rerun_documents_scope_bidirectional` accepted `19/19` - `address_truth_harness_phase82_human_mixed_integrity_status_dialog_post_m23_rerun_documents_scope_bidirectional` accepted `19/19`
- `address_truth_harness_phase82_human_mixed_integrity_status_dialog_post_f_account_injection_guard_clean_scope` accepted `19/19`, with the `Жуковке 51` numeric counterparty suffix kept as counterparty scope instead of leaking as account `51` - `address_truth_harness_phase82_human_mixed_integrity_status_dialog_post_f_account_injection_guard_clean_scope` accepted `19/19`, with the `Жуковке 51` numeric counterparty suffix kept as counterparty scope instead of leaking as account `51`
- `address_truth_harness_post_f_cross_stage_canary_agent_20260424_live5` accepted `24/24`, proving a saved cross-stage AGENT canary across VAT metadata, numeric counterparty suffixes, open-organization value-flow clarification, ranked value-flow year switches, and SVK grounded reset; the saved autorun is `AGENT | Post-F cross-stage semantic integrity canary` (`gen-ag04241406-abe4d8`) - `address_truth_harness_post_f_cross_stage_canary_agent_20260424_live7` accepted `24/24`, proving a saved cross-stage AGENT canary across VAT metadata, metadata-scoped organization/document pivots, numeric counterparty suffixes, open-organization value-flow clarification, ranked value-flow year switches, and SVK grounded reset; the saved autorun is `AGENT | Post-F cross-stage semantic integrity canary` (`gen-ag04241406-abe4d8`)
- `address_truth_harness_phase11_manual_followup_meta_quality_live_rerun_vatfix` accepted `10/10` - `address_truth_harness_phase11_manual_followup_meta_quality_live_rerun_vatfix` accepted `10/10`
- `address_truth_harness_phase20_continuity_stabilization_live_rerun_vatfix` accepted `6/6` - `address_truth_harness_phase20_continuity_stabilization_live_rerun_vatfix` accepted `6/6`
- `addressQueryRuntimeM23.test.ts` full semantic/runtime slice accepted `403/403` after Post-F VAT/date-basis, scope-recovery, open value-flow organization clarification, document-vs-bank arbitration, and reply-shape hardening - `addressQueryRuntimeM23.test.ts` full semantic/runtime slice accepted `403/403` after Post-F VAT/date-basis, scope-recovery, open value-flow organization clarification, document-vs-bank arbitration, and reply-shape hardening

View File

@ -89,8 +89,17 @@ function isValueFlowPredicateEntityCandidate(value) {
} }
return !/(?<!\p{L})(?:ооо|ип|ао|пао|зао|llc|inc|corp)(?!\p{L})/iu.test(text); return !/(?<!\p{L})(?:ооо|ип|ао|пао|зао|llc|inc|corp)(?!\p{L})/iu.test(text);
} }
function isActionVerbEntityCandidate(value) {
if (!value) {
return false;
}
return new Set(["провести", "показать", "посмотреть", "смотреть", "найти", "искать", "анализ"]).has((0, addressTextRepair_1.normalizeRussianComparableText)(value));
}
function isInvalidEntityCandidate(value) { function isInvalidEntityCandidate(value) {
return Boolean(value && (isReferentialEntityPlaceholder(value) || isValueFlowPredicateEntityCandidate(value))); return Boolean(value &&
(isReferentialEntityPlaceholder(value) ||
isValueFlowPredicateEntityCandidate(value) ||
isActionVerbEntityCandidate(value)));
} }
function normalizeFollowupCounterpartyCandidate(value) { function normalizeFollowupCounterpartyCandidate(value) {
const text = candidateValue(value); const text = candidateValue(value);
@ -866,9 +875,20 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
const rawOrganizationScope = extractOrganizationScopeFromRawText(rawUserText ?? rawEffectiveText ?? rawSignalSourceText); const rawOrganizationScope = extractOrganizationScopeFromRawText(rawUserText ?? rawEffectiveText ?? rawSignalSourceText);
const currentTurnFreshOrganizationScope = rawOrganizationScope ?? predecomposeEntities.organization; const currentTurnFreshOrganizationScope = rawOrganizationScope ?? predecomposeEntities.organization;
const currentTurnOrganizationScope = currentTurnFreshOrganizationScope ?? assistantTurnMeaningOrganizationScope; const currentTurnOrganizationScope = currentTurnFreshOrganizationScope ?? assistantTurnMeaningOrganizationScope;
const followupCounterpartyIsMetadataOrganizationScope = Boolean(followupSeed.subjectResolutionOptional &&
followupSeed.counterparty &&
(followupSeed.metadataScopeHint ||
followupSeed.metadataSelectedEntitySet ||
followupSeed.metadataSelectedSurfaceObjects.length > 0) &&
(isInvalidEntityCandidate(followupSeed.counterparty) ||
sameScopedName(followupSeed.counterparty, followupSeed.organization) ||
sameScopedName(followupSeed.counterparty, currentTurnOrganizationScope)));
const effectiveFollowupCounterparty = followupCounterpartyIsMetadataOrganizationScope
? null
: followupSeed.counterparty;
const explicitOrganizationScopeSignal = Boolean(rawOrganizationMentionSignal && currentTurnOrganizationScope); const explicitOrganizationScopeSignal = Boolean(rawOrganizationMentionSignal && currentTurnOrganizationScope);
const organizationClarificationFollowupApplicable = Boolean(followupSeed.domain === "counterparty_value" && const organizationClarificationFollowupApplicable = Boolean(followupSeed.domain === "counterparty_value" &&
!followupSeed.counterparty && !effectiveFollowupCounterparty &&
currentTurnOrganizationScope && currentTurnOrganizationScope &&
!rawLifecycleSignal && !rawLifecycleSignal &&
!rawValueFlowSignal && !rawValueFlowSignal &&
@ -912,7 +932,7 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
!rawLifecycleSignal && !rawLifecycleSignal &&
!rawValueFlowSignal && !rawValueFlowSignal &&
hasMetadataObjectHint(rawText)); hasMetadataObjectHint(rawText));
const metadataLaneCarryoverAvailable = Boolean(followupSeed.counterparty || const metadataLaneCarryoverAvailable = Boolean(effectiveFollowupCounterparty ||
followupSeed.discoveryEntity || followupSeed.discoveryEntity ||
followupSeed.metadataScopeHint || followupSeed.metadataScopeHint ||
followupSeed.metadataSelectedEntitySet || followupSeed.metadataSelectedEntitySet ||
@ -946,7 +966,7 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
!rawLifecycleSignal && !rawLifecycleSignal &&
metadataMovementHintSignal); metadataMovementHintSignal);
const entityResolutionGroundedDocumentFollowupApplicable = Boolean(followupSeed.pilotScope === "entity_resolution_search_v1" && const entityResolutionGroundedDocumentFollowupApplicable = Boolean(followupSeed.pilotScope === "entity_resolution_search_v1" &&
(followupSeed.counterparty || followupSeed.discoveryEntity) && (effectiveFollowupCounterparty || followupSeed.discoveryEntity) &&
!rawLifecycleSignal && !rawLifecycleSignal &&
!rawValueFlowSignal && !rawValueFlowSignal &&
!rawMetadataSignal && !rawMetadataSignal &&
@ -959,7 +979,7 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
!rawMetadataSignal && !rawMetadataSignal &&
metadataDocumentHintSignal); metadataDocumentHintSignal);
const entityResolutionGroundedMovementFollowupApplicable = Boolean(followupSeed.pilotScope === "entity_resolution_search_v1" && const entityResolutionGroundedMovementFollowupApplicable = Boolean(followupSeed.pilotScope === "entity_resolution_search_v1" &&
(followupSeed.counterparty || followupSeed.discoveryEntity) && (effectiveFollowupCounterparty || followupSeed.discoveryEntity) &&
!rawLifecycleSignal && !rawLifecycleSignal &&
!rawValueFlowSignal && !rawValueFlowSignal &&
!rawMetadataSignal && !rawMetadataSignal &&
@ -974,14 +994,14 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
const groundedValueFlowFollowupApplicable = Boolean(rawValueFlowSignal && const groundedValueFlowFollowupApplicable = Boolean(rawValueFlowSignal &&
!rawLifecycleSignal && !rawLifecycleSignal &&
!rawMetadataSignal && !rawMetadataSignal &&
(followupSeed.counterparty || followupSeed.discoveryEntity) && (effectiveFollowupCounterparty || followupSeed.discoveryEntity) &&
(followupSeed.pilotScope === "entity_resolution_search_v1" || (followupSeed.pilotScope === "entity_resolution_search_v1" ||
followupSeed.pilotScope === "counterparty_document_evidence_query_documents_v1" || followupSeed.pilotScope === "counterparty_document_evidence_query_documents_v1" ||
followupSeed.pilotScope === "counterparty_movement_evidence_query_movements_v1" || followupSeed.pilotScope === "counterparty_movement_evidence_query_movements_v1" ||
followupSeed.pilotScope === "counterparty_value_flow_query_movements_v1" || followupSeed.pilotScope === "counterparty_value_flow_query_movements_v1" ||
followupSeed.pilotScope === "counterparty_supplier_payout_query_movements_v1" || followupSeed.pilotScope === "counterparty_supplier_payout_query_movements_v1" ||
followupSeed.pilotScope === "counterparty_bidirectional_value_flow_query_movements_v1")); followupSeed.pilotScope === "counterparty_bidirectional_value_flow_query_movements_v1"));
const groundedValueFlowEvidenceSourceApplicable = Boolean((followupSeed.counterparty || followupSeed.discoveryEntity) && const groundedValueFlowEvidenceSourceApplicable = Boolean((effectiveFollowupCounterparty || followupSeed.discoveryEntity) &&
!rawLifecycleSignal && !rawLifecycleSignal &&
!rawValueFlowSignal && !rawValueFlowSignal &&
!rawMetadataSignal && !rawMetadataSignal &&
@ -995,27 +1015,27 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
const valueFlowGroundedDocumentFollowupApplicable = Boolean(groundedValueFlowEvidenceSourceApplicable && metadataDocumentHintSignal); const valueFlowGroundedDocumentFollowupApplicable = Boolean(groundedValueFlowEvidenceSourceApplicable && metadataDocumentHintSignal);
const valueFlowGroundedMovementFollowupApplicable = Boolean(groundedValueFlowEvidenceSourceApplicable && metadataMovementHintSignal); const valueFlowGroundedMovementFollowupApplicable = Boolean(groundedValueFlowEvidenceSourceApplicable && metadataMovementHintSignal);
const documentEvidenceGroundedMovementFollowupApplicable = Boolean(followupSeed.pilotScope === "counterparty_document_evidence_query_documents_v1" && const documentEvidenceGroundedMovementFollowupApplicable = Boolean(followupSeed.pilotScope === "counterparty_document_evidence_query_documents_v1" &&
(followupSeed.counterparty || followupSeed.discoveryEntity) && (effectiveFollowupCounterparty || followupSeed.discoveryEntity) &&
!rawLifecycleSignal && !rawLifecycleSignal &&
!rawValueFlowSignal && !rawValueFlowSignal &&
!rawMetadataSignal && !rawMetadataSignal &&
metadataMovementHintSignal); metadataMovementHintSignal);
const movementEvidenceGroundedDocumentFollowupApplicable = Boolean(followupSeed.pilotScope === "counterparty_movement_evidence_query_movements_v1" && const movementEvidenceGroundedDocumentFollowupApplicable = Boolean(followupSeed.pilotScope === "counterparty_movement_evidence_query_movements_v1" &&
(followupSeed.counterparty || followupSeed.discoveryEntity) && (effectiveFollowupCounterparty || followupSeed.discoveryEntity) &&
!rawLifecycleSignal && !rawLifecycleSignal &&
!rawValueFlowSignal && !rawValueFlowSignal &&
!rawMetadataSignal && !rawMetadataSignal &&
metadataDocumentHintSignal); metadataDocumentHintSignal);
const metadataScopedMovementEvidenceToDocumentFollowupApplicable = Boolean(followupSeed.pilotScope === "counterparty_movement_evidence_query_movements_v1" && const metadataScopedMovementEvidenceToDocumentFollowupApplicable = Boolean(followupSeed.pilotScope === "counterparty_movement_evidence_query_movements_v1" &&
followupSeed.subjectResolutionOptional && followupSeed.subjectResolutionOptional &&
!followupSeed.counterparty && !effectiveFollowupCounterparty &&
metadataLaneCarryoverAvailable && metadataLaneCarryoverAvailable &&
!rawLifecycleSignal && !rawLifecycleSignal &&
!rawValueFlowSignal && !rawValueFlowSignal &&
metadataDocumentHintSignal); metadataDocumentHintSignal);
const metadataScopedDocumentEvidenceToMovementFollowupApplicable = Boolean(followupSeed.pilotScope === "counterparty_document_evidence_query_documents_v1" && const metadataScopedDocumentEvidenceToMovementFollowupApplicable = Boolean(followupSeed.pilotScope === "counterparty_document_evidence_query_documents_v1" &&
followupSeed.subjectResolutionOptional && followupSeed.subjectResolutionOptional &&
!followupSeed.counterparty && !effectiveFollowupCounterparty &&
metadataLaneCarryoverAvailable && metadataLaneCarryoverAvailable &&
!rawLifecycleSignal && !rawLifecycleSignal &&
!rawValueFlowSignal && !rawValueFlowSignal &&
@ -1074,7 +1094,7 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
const metadataGroundedDocumentLaneApplicable = metadataGroundedDocumentFollowupApplicable || const metadataGroundedDocumentLaneApplicable = metadataGroundedDocumentFollowupApplicable ||
metadataAmbiguityResolvedDocumentFollowupApplicable || metadataAmbiguityResolvedDocumentFollowupApplicable ||
(followupSeed.subjectResolutionOptional && (followupSeed.subjectResolutionOptional &&
!followupSeed.counterparty && !effectiveFollowupCounterparty &&
metadataLaneCarryoverAvailable && metadataLaneCarryoverAvailable &&
!rawLifecycleSignal && !rawLifecycleSignal &&
!rawValueFlowSignal && !rawValueFlowSignal &&
@ -1092,7 +1112,7 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
const metadataGroundedMovementLaneApplicable = metadataGroundedMovementFollowupApplicable || const metadataGroundedMovementLaneApplicable = metadataGroundedMovementFollowupApplicable ||
metadataAmbiguityResolvedMovementFollowupApplicable || metadataAmbiguityResolvedMovementFollowupApplicable ||
(followupSeed.subjectResolutionOptional && (followupSeed.subjectResolutionOptional &&
!followupSeed.counterparty && !effectiveFollowupCounterparty &&
metadataLaneCarryoverAvailable && metadataLaneCarryoverAvailable &&
!rawLifecycleSignal && !rawLifecycleSignal &&
!rawValueFlowSignal && !rawValueFlowSignal &&
@ -1171,8 +1191,8 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
? normalizedPredecomposeCounterparty ? normalizedPredecomposeCounterparty
: null; : null;
const explicitCurrentCounterpartyOverridesFollowupEntity = Boolean(explicitCurrentCounterpartyCandidate && const explicitCurrentCounterpartyOverridesFollowupEntity = Boolean(explicitCurrentCounterpartyCandidate &&
(followupSeed.counterparty || followupSeed.discoveryEntity) && (effectiveFollowupCounterparty || followupSeed.discoveryEntity) &&
!sameScopedName(explicitCurrentCounterpartyCandidate, followupSeed.counterparty ?? followupSeed.discoveryEntity) && !sameScopedName(explicitCurrentCounterpartyCandidate, effectiveFollowupCounterparty ?? followupSeed.discoveryEntity) &&
(valueFlowSignal || (valueFlowSignal ||
lifecycleSignal || lifecycleSignal ||
metadataGroundedDocumentLaneApplicable || metadataGroundedDocumentLaneApplicable ||
@ -1187,7 +1207,7 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
followupSeed.metadataSelectedEntitySet ?? followupSeed.metadataSelectedEntitySet ??
null; null;
const metadataScopedLaneWithoutSubject = Boolean((metadataGroundedMovementLaneApplicable || metadataGroundedDocumentLaneApplicable) && const metadataScopedLaneWithoutSubject = Boolean((metadataGroundedMovementLaneApplicable || metadataGroundedDocumentLaneApplicable) &&
!followupSeed.counterparty && !effectiveFollowupCounterparty &&
metadataLaneCarryoverAvailable); metadataLaneCarryoverAvailable);
const groundedFollowupEntity = metadataScopedLaneWithoutSubject const groundedFollowupEntity = metadataScopedLaneWithoutSubject
? null ? null
@ -1195,7 +1215,7 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
? null ? null
: explicitCurrentCounterpartyOverridesFollowupEntity : explicitCurrentCounterpartyOverridesFollowupEntity
? null ? null
: followupSeed.counterparty ?? followupSeed.discoveryEntity; : effectiveFollowupCounterparty ?? followupSeed.discoveryEntity;
const entityCandidates = entityResolutionSignal ? [] : []; const entityCandidates = entityResolutionSignal ? [] : [];
if (entityResolutionSignal) { if (entityResolutionSignal) {
pushNormalizedEntityResolutionCandidate(entityCandidates, entityResolutionClarificationCandidate); pushNormalizedEntityResolutionCandidate(entityCandidates, entityResolutionClarificationCandidate);
@ -1209,7 +1229,7 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
pushNormalizedEntityResolutionCandidate(entityCandidates, normalizedPredecomposeCounterparty); pushNormalizedEntityResolutionCandidate(entityCandidates, normalizedPredecomposeCounterparty);
} }
if (!rawEntitySearchOverridesStaleScope) { if (!rawEntitySearchOverridesStaleScope) {
pushNormalizedEntityResolutionCandidate(entityCandidates, followupSeed.counterparty); pushNormalizedEntityResolutionCandidate(entityCandidates, effectiveFollowupCounterparty);
} }
} }
else { else {
@ -1221,7 +1241,7 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
} }
pushScopedEntityCandidate(entityCandidates, normalizedPredecomposeCounterparty, groundedFollowupEntity); pushScopedEntityCandidate(entityCandidates, normalizedPredecomposeCounterparty, groundedFollowupEntity);
if (!groundedFollowupEntity) { if (!groundedFollowupEntity) {
pushScopedEntityCandidate(entityCandidates, followupSeed.counterparty, null); pushScopedEntityCandidate(entityCandidates, effectiveFollowupCounterparty, null);
if (!metadataScopedLaneWithoutSubject) { if (!metadataScopedLaneWithoutSubject) {
pushScopedEntityCandidate(entityCandidates, followupSeed.discoveryEntity, null); pushScopedEntityCandidate(entityCandidates, followupSeed.discoveryEntity, null);
} }
@ -1234,7 +1254,7 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
pushUnique(entityCandidates, followupSeed.discoveryEntity); pushUnique(entityCandidates, followupSeed.discoveryEntity);
pushUnique(entityCandidates, rawMetadataScopeHint); pushUnique(entityCandidates, rawMetadataScopeHint);
} }
const openScopeValueFlowWithoutCounterparty = valueFlowSignal && !normalizedPredecomposeCounterparty && !followupSeed.counterparty; const openScopeValueFlowWithoutCounterparty = valueFlowSignal && !normalizedPredecomposeCounterparty && !effectiveFollowupCounterparty;
const valueFlowOrganizationStaysScope = openScopeValueFlowWithoutCounterparty && const valueFlowOrganizationStaysScope = openScopeValueFlowWithoutCounterparty &&
Boolean(bidirectionalValueFlowSignal || Boolean(bidirectionalValueFlowSignal ||
hasValueRankingSignal(rawText) || hasValueRankingSignal(rawText) ||
@ -1242,7 +1262,7 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
explicitOrganizationScopeSignal || explicitOrganizationScopeSignal ||
organizationClarificationFollowupApplicable || organizationClarificationFollowupApplicable ||
followupSeed.organization); followupSeed.organization);
const openScopeValueFlowWithoutResolvedCounterparty = Boolean(valueFlowSignal && !normalizedPredecomposeCounterparty && !followupSeed.counterparty); const openScopeValueFlowWithoutResolvedCounterparty = Boolean(valueFlowSignal && !normalizedPredecomposeCounterparty && !effectiveFollowupCounterparty);
if (openScopeValueFlowWithoutCounterparty && !valueFlowOrganizationStaysScope) { if (openScopeValueFlowWithoutCounterparty && !valueFlowOrganizationStaysScope) {
pushUnique(entityCandidates, predecomposeEntities.organization); pushUnique(entityCandidates, predecomposeEntities.organization);
pushUnique(entityCandidates, followupSeed.organization); pushUnique(entityCandidates, followupSeed.organization);
@ -1263,9 +1283,9 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
if ((metadataLaneScopeHint && sameScopedName(candidate, metadataLaneScopeHint)) || if ((metadataLaneScopeHint && sameScopedName(candidate, metadataLaneScopeHint)) ||
(explicitOrganizationScope && sameScopedName(candidate, explicitOrganizationScope)) || (explicitOrganizationScope && sameScopedName(candidate, explicitOrganizationScope)) ||
(followupSeed.organization && sameScopedName(candidate, followupSeed.organization)) || (followupSeed.organization && sameScopedName(candidate, followupSeed.organization)) ||
(followupSeed.counterparty && (effectiveFollowupCounterparty &&
!sameScopedName(followupSeed.counterparty, explicitCurrentCounterpartyCandidate) && !sameScopedName(effectiveFollowupCounterparty, explicitCurrentCounterpartyCandidate) &&
sameScopedName(candidate, followupSeed.counterparty))) { sameScopedName(candidate, effectiveFollowupCounterparty))) {
entityCandidates.splice(index, 1); entityCandidates.splice(index, 1);
} }
} }
@ -1590,7 +1610,7 @@ function buildAssistantMcpDiscoveryTurnInput(input) {
normalizedPredecomposeCounterparty) { normalizedPredecomposeCounterparty) {
pushReason(reasonCodes, "mcp_discovery_counterparty_from_predecompose"); pushReason(reasonCodes, "mcp_discovery_counterparty_from_predecompose");
} }
if (followupSeed.counterparty && !rawEntitySearchOverridesStaleScope) { if (effectiveFollowupCounterparty && !rawEntitySearchOverridesStaleScope) {
pushReason(reasonCodes, "mcp_discovery_counterparty_from_followup_context"); pushReason(reasonCodes, "mcp_discovery_counterparty_from_followup_context");
} }
if (followupDateScopeApplied) { if (followupDateScopeApplied) {

View File

@ -135,8 +135,22 @@ function isValueFlowPredicateEntityCandidate(value: string | null): boolean {
return !/(?<!\p{L})(?:ооо|ип|ао|пао|зао|llc|inc|corp)(?!\p{L})/iu.test(text); return !/(?<!\p{L})(?:ооо|ип|ао|пао|зао|llc|inc|corp)(?!\p{L})/iu.test(text);
} }
function isActionVerbEntityCandidate(value: string | null): boolean {
if (!value) {
return false;
}
return new Set(["провести", "показать", "посмотреть", "смотреть", "найти", "искать", "анализ"]).has(
normalizeRussianComparableText(value)
);
}
function isInvalidEntityCandidate(value: string | null): boolean { function isInvalidEntityCandidate(value: string | null): boolean {
return Boolean(value && (isReferentialEntityPlaceholder(value) || isValueFlowPredicateEntityCandidate(value))); return Boolean(
value &&
(isReferentialEntityPlaceholder(value) ||
isValueFlowPredicateEntityCandidate(value) ||
isActionVerbEntityCandidate(value))
);
} }
function normalizeFollowupCounterpartyCandidate(value: unknown): string | null { function normalizeFollowupCounterpartyCandidate(value: unknown): string | null {
@ -1163,10 +1177,23 @@ export function buildAssistantMcpDiscoveryTurnInput(
const currentTurnFreshOrganizationScope = rawOrganizationScope ?? predecomposeEntities.organization; const currentTurnFreshOrganizationScope = rawOrganizationScope ?? predecomposeEntities.organization;
const currentTurnOrganizationScope = const currentTurnOrganizationScope =
currentTurnFreshOrganizationScope ?? assistantTurnMeaningOrganizationScope; currentTurnFreshOrganizationScope ?? assistantTurnMeaningOrganizationScope;
const followupCounterpartyIsMetadataOrganizationScope = Boolean(
followupSeed.subjectResolutionOptional &&
followupSeed.counterparty &&
(followupSeed.metadataScopeHint ||
followupSeed.metadataSelectedEntitySet ||
followupSeed.metadataSelectedSurfaceObjects.length > 0) &&
(isInvalidEntityCandidate(followupSeed.counterparty) ||
sameScopedName(followupSeed.counterparty, followupSeed.organization) ||
sameScopedName(followupSeed.counterparty, currentTurnOrganizationScope))
);
const effectiveFollowupCounterparty = followupCounterpartyIsMetadataOrganizationScope
? null
: followupSeed.counterparty;
const explicitOrganizationScopeSignal = Boolean(rawOrganizationMentionSignal && currentTurnOrganizationScope); const explicitOrganizationScopeSignal = Boolean(rawOrganizationMentionSignal && currentTurnOrganizationScope);
const organizationClarificationFollowupApplicable = Boolean( const organizationClarificationFollowupApplicable = Boolean(
followupSeed.domain === "counterparty_value" && followupSeed.domain === "counterparty_value" &&
!followupSeed.counterparty && !effectiveFollowupCounterparty &&
currentTurnOrganizationScope && currentTurnOrganizationScope &&
!rawLifecycleSignal && !rawLifecycleSignal &&
!rawValueFlowSignal && !rawValueFlowSignal &&
@ -1225,7 +1252,7 @@ export function buildAssistantMcpDiscoveryTurnInput(
hasMetadataObjectHint(rawText) hasMetadataObjectHint(rawText)
); );
const metadataLaneCarryoverAvailable = Boolean( const metadataLaneCarryoverAvailable = Boolean(
followupSeed.counterparty || effectiveFollowupCounterparty ||
followupSeed.discoveryEntity || followupSeed.discoveryEntity ||
followupSeed.metadataScopeHint || followupSeed.metadataScopeHint ||
followupSeed.metadataSelectedEntitySet || followupSeed.metadataSelectedEntitySet ||
@ -1269,7 +1296,7 @@ export function buildAssistantMcpDiscoveryTurnInput(
); );
const entityResolutionGroundedDocumentFollowupApplicable = Boolean( const entityResolutionGroundedDocumentFollowupApplicable = Boolean(
followupSeed.pilotScope === "entity_resolution_search_v1" && followupSeed.pilotScope === "entity_resolution_search_v1" &&
(followupSeed.counterparty || followupSeed.discoveryEntity) && (effectiveFollowupCounterparty || followupSeed.discoveryEntity) &&
!rawLifecycleSignal && !rawLifecycleSignal &&
!rawValueFlowSignal && !rawValueFlowSignal &&
!rawMetadataSignal && !rawMetadataSignal &&
@ -1286,7 +1313,7 @@ export function buildAssistantMcpDiscoveryTurnInput(
); );
const entityResolutionGroundedMovementFollowupApplicable = Boolean( const entityResolutionGroundedMovementFollowupApplicable = Boolean(
followupSeed.pilotScope === "entity_resolution_search_v1" && followupSeed.pilotScope === "entity_resolution_search_v1" &&
(followupSeed.counterparty || followupSeed.discoveryEntity) && (effectiveFollowupCounterparty || followupSeed.discoveryEntity) &&
!rawLifecycleSignal && !rawLifecycleSignal &&
!rawValueFlowSignal && !rawValueFlowSignal &&
!rawMetadataSignal && !rawMetadataSignal &&
@ -1305,7 +1332,7 @@ export function buildAssistantMcpDiscoveryTurnInput(
rawValueFlowSignal && rawValueFlowSignal &&
!rawLifecycleSignal && !rawLifecycleSignal &&
!rawMetadataSignal && !rawMetadataSignal &&
(followupSeed.counterparty || followupSeed.discoveryEntity) && (effectiveFollowupCounterparty || followupSeed.discoveryEntity) &&
(followupSeed.pilotScope === "entity_resolution_search_v1" || (followupSeed.pilotScope === "entity_resolution_search_v1" ||
followupSeed.pilotScope === "counterparty_document_evidence_query_documents_v1" || followupSeed.pilotScope === "counterparty_document_evidence_query_documents_v1" ||
followupSeed.pilotScope === "counterparty_movement_evidence_query_movements_v1" || followupSeed.pilotScope === "counterparty_movement_evidence_query_movements_v1" ||
@ -1314,7 +1341,7 @@ export function buildAssistantMcpDiscoveryTurnInput(
followupSeed.pilotScope === "counterparty_bidirectional_value_flow_query_movements_v1") followupSeed.pilotScope === "counterparty_bidirectional_value_flow_query_movements_v1")
); );
const groundedValueFlowEvidenceSourceApplicable = Boolean( const groundedValueFlowEvidenceSourceApplicable = Boolean(
(followupSeed.counterparty || followupSeed.discoveryEntity) && (effectiveFollowupCounterparty || followupSeed.discoveryEntity) &&
!rawLifecycleSignal && !rawLifecycleSignal &&
!rawValueFlowSignal && !rawValueFlowSignal &&
!rawMetadataSignal && !rawMetadataSignal &&
@ -1334,7 +1361,7 @@ export function buildAssistantMcpDiscoveryTurnInput(
); );
const documentEvidenceGroundedMovementFollowupApplicable = Boolean( const documentEvidenceGroundedMovementFollowupApplicable = Boolean(
followupSeed.pilotScope === "counterparty_document_evidence_query_documents_v1" && followupSeed.pilotScope === "counterparty_document_evidence_query_documents_v1" &&
(followupSeed.counterparty || followupSeed.discoveryEntity) && (effectiveFollowupCounterparty || followupSeed.discoveryEntity) &&
!rawLifecycleSignal && !rawLifecycleSignal &&
!rawValueFlowSignal && !rawValueFlowSignal &&
!rawMetadataSignal && !rawMetadataSignal &&
@ -1342,7 +1369,7 @@ export function buildAssistantMcpDiscoveryTurnInput(
); );
const movementEvidenceGroundedDocumentFollowupApplicable = Boolean( const movementEvidenceGroundedDocumentFollowupApplicable = Boolean(
followupSeed.pilotScope === "counterparty_movement_evidence_query_movements_v1" && followupSeed.pilotScope === "counterparty_movement_evidence_query_movements_v1" &&
(followupSeed.counterparty || followupSeed.discoveryEntity) && (effectiveFollowupCounterparty || followupSeed.discoveryEntity) &&
!rawLifecycleSignal && !rawLifecycleSignal &&
!rawValueFlowSignal && !rawValueFlowSignal &&
!rawMetadataSignal && !rawMetadataSignal &&
@ -1351,7 +1378,7 @@ export function buildAssistantMcpDiscoveryTurnInput(
const metadataScopedMovementEvidenceToDocumentFollowupApplicable = Boolean( const metadataScopedMovementEvidenceToDocumentFollowupApplicable = Boolean(
followupSeed.pilotScope === "counterparty_movement_evidence_query_movements_v1" && followupSeed.pilotScope === "counterparty_movement_evidence_query_movements_v1" &&
followupSeed.subjectResolutionOptional && followupSeed.subjectResolutionOptional &&
!followupSeed.counterparty && !effectiveFollowupCounterparty &&
metadataLaneCarryoverAvailable && metadataLaneCarryoverAvailable &&
!rawLifecycleSignal && !rawLifecycleSignal &&
!rawValueFlowSignal && !rawValueFlowSignal &&
@ -1360,7 +1387,7 @@ export function buildAssistantMcpDiscoveryTurnInput(
const metadataScopedDocumentEvidenceToMovementFollowupApplicable = Boolean( const metadataScopedDocumentEvidenceToMovementFollowupApplicable = Boolean(
followupSeed.pilotScope === "counterparty_document_evidence_query_documents_v1" && followupSeed.pilotScope === "counterparty_document_evidence_query_documents_v1" &&
followupSeed.subjectResolutionOptional && followupSeed.subjectResolutionOptional &&
!followupSeed.counterparty && !effectiveFollowupCounterparty &&
metadataLaneCarryoverAvailable && metadataLaneCarryoverAvailable &&
!rawLifecycleSignal && !rawLifecycleSignal &&
!rawValueFlowSignal && !rawValueFlowSignal &&
@ -1431,7 +1458,7 @@ export function buildAssistantMcpDiscoveryTurnInput(
metadataGroundedDocumentFollowupApplicable || metadataGroundedDocumentFollowupApplicable ||
metadataAmbiguityResolvedDocumentFollowupApplicable || metadataAmbiguityResolvedDocumentFollowupApplicable ||
(followupSeed.subjectResolutionOptional && (followupSeed.subjectResolutionOptional &&
!followupSeed.counterparty && !effectiveFollowupCounterparty &&
metadataLaneCarryoverAvailable && metadataLaneCarryoverAvailable &&
!rawLifecycleSignal && !rawLifecycleSignal &&
!rawValueFlowSignal && !rawValueFlowSignal &&
@ -1450,7 +1477,7 @@ export function buildAssistantMcpDiscoveryTurnInput(
metadataGroundedMovementFollowupApplicable || metadataGroundedMovementFollowupApplicable ||
metadataAmbiguityResolvedMovementFollowupApplicable || metadataAmbiguityResolvedMovementFollowupApplicable ||
(followupSeed.subjectResolutionOptional && (followupSeed.subjectResolutionOptional &&
!followupSeed.counterparty && !effectiveFollowupCounterparty &&
metadataLaneCarryoverAvailable && metadataLaneCarryoverAvailable &&
!rawLifecycleSignal && !rawLifecycleSignal &&
!rawValueFlowSignal && !rawValueFlowSignal &&
@ -1537,8 +1564,8 @@ export function buildAssistantMcpDiscoveryTurnInput(
: null; : null;
const explicitCurrentCounterpartyOverridesFollowupEntity = Boolean( const explicitCurrentCounterpartyOverridesFollowupEntity = Boolean(
explicitCurrentCounterpartyCandidate && explicitCurrentCounterpartyCandidate &&
(followupSeed.counterparty || followupSeed.discoveryEntity) && (effectiveFollowupCounterparty || followupSeed.discoveryEntity) &&
!sameScopedName(explicitCurrentCounterpartyCandidate, followupSeed.counterparty ?? followupSeed.discoveryEntity) && !sameScopedName(explicitCurrentCounterpartyCandidate, effectiveFollowupCounterparty ?? followupSeed.discoveryEntity) &&
(valueFlowSignal || (valueFlowSignal ||
lifecycleSignal || lifecycleSignal ||
metadataGroundedDocumentLaneApplicable || metadataGroundedDocumentLaneApplicable ||
@ -1556,7 +1583,7 @@ export function buildAssistantMcpDiscoveryTurnInput(
null; null;
const metadataScopedLaneWithoutSubject = Boolean( const metadataScopedLaneWithoutSubject = Boolean(
(metadataGroundedMovementLaneApplicable || metadataGroundedDocumentLaneApplicable) && (metadataGroundedMovementLaneApplicable || metadataGroundedDocumentLaneApplicable) &&
!followupSeed.counterparty && !effectiveFollowupCounterparty &&
metadataLaneCarryoverAvailable metadataLaneCarryoverAvailable
); );
const groundedFollowupEntity = metadataScopedLaneWithoutSubject const groundedFollowupEntity = metadataScopedLaneWithoutSubject
@ -1565,7 +1592,7 @@ export function buildAssistantMcpDiscoveryTurnInput(
? null ? null
: explicitCurrentCounterpartyOverridesFollowupEntity : explicitCurrentCounterpartyOverridesFollowupEntity
? null ? null
: followupSeed.counterparty ?? followupSeed.discoveryEntity; : effectiveFollowupCounterparty ?? followupSeed.discoveryEntity;
const entityCandidates = entityResolutionSignal ? [] : []; const entityCandidates = entityResolutionSignal ? [] : [];
if (entityResolutionSignal) { if (entityResolutionSignal) {
pushNormalizedEntityResolutionCandidate(entityCandidates, entityResolutionClarificationCandidate); pushNormalizedEntityResolutionCandidate(entityCandidates, entityResolutionClarificationCandidate);
@ -1579,7 +1606,7 @@ export function buildAssistantMcpDiscoveryTurnInput(
pushNormalizedEntityResolutionCandidate(entityCandidates, normalizedPredecomposeCounterparty); pushNormalizedEntityResolutionCandidate(entityCandidates, normalizedPredecomposeCounterparty);
} }
if (!rawEntitySearchOverridesStaleScope) { if (!rawEntitySearchOverridesStaleScope) {
pushNormalizedEntityResolutionCandidate(entityCandidates, followupSeed.counterparty); pushNormalizedEntityResolutionCandidate(entityCandidates, effectiveFollowupCounterparty);
} }
} else { } else {
if (groundedFollowupEntity) { if (groundedFollowupEntity) {
@ -1590,7 +1617,7 @@ export function buildAssistantMcpDiscoveryTurnInput(
} }
pushScopedEntityCandidate(entityCandidates, normalizedPredecomposeCounterparty, groundedFollowupEntity); pushScopedEntityCandidate(entityCandidates, normalizedPredecomposeCounterparty, groundedFollowupEntity);
if (!groundedFollowupEntity) { if (!groundedFollowupEntity) {
pushScopedEntityCandidate(entityCandidates, followupSeed.counterparty, null); pushScopedEntityCandidate(entityCandidates, effectiveFollowupCounterparty, null);
if (!metadataScopedLaneWithoutSubject) { if (!metadataScopedLaneWithoutSubject) {
pushScopedEntityCandidate(entityCandidates, followupSeed.discoveryEntity, null); pushScopedEntityCandidate(entityCandidates, followupSeed.discoveryEntity, null);
} }
@ -1606,7 +1633,7 @@ export function buildAssistantMcpDiscoveryTurnInput(
pushUnique(entityCandidates, rawMetadataScopeHint); pushUnique(entityCandidates, rawMetadataScopeHint);
} }
const openScopeValueFlowWithoutCounterparty = const openScopeValueFlowWithoutCounterparty =
valueFlowSignal && !normalizedPredecomposeCounterparty && !followupSeed.counterparty; valueFlowSignal && !normalizedPredecomposeCounterparty && !effectiveFollowupCounterparty;
const valueFlowOrganizationStaysScope = const valueFlowOrganizationStaysScope =
openScopeValueFlowWithoutCounterparty && openScopeValueFlowWithoutCounterparty &&
Boolean( Boolean(
@ -1618,7 +1645,7 @@ export function buildAssistantMcpDiscoveryTurnInput(
followupSeed.organization followupSeed.organization
); );
const openScopeValueFlowWithoutResolvedCounterparty = Boolean( const openScopeValueFlowWithoutResolvedCounterparty = Boolean(
valueFlowSignal && !normalizedPredecomposeCounterparty && !followupSeed.counterparty valueFlowSignal && !normalizedPredecomposeCounterparty && !effectiveFollowupCounterparty
); );
if (openScopeValueFlowWithoutCounterparty && !valueFlowOrganizationStaysScope) { if (openScopeValueFlowWithoutCounterparty && !valueFlowOrganizationStaysScope) {
pushUnique(entityCandidates, predecomposeEntities.organization); pushUnique(entityCandidates, predecomposeEntities.organization);
@ -1644,9 +1671,9 @@ export function buildAssistantMcpDiscoveryTurnInput(
(metadataLaneScopeHint && sameScopedName(candidate, metadataLaneScopeHint)) || (metadataLaneScopeHint && sameScopedName(candidate, metadataLaneScopeHint)) ||
(explicitOrganizationScope && sameScopedName(candidate, explicitOrganizationScope)) || (explicitOrganizationScope && sameScopedName(candidate, explicitOrganizationScope)) ||
(followupSeed.organization && sameScopedName(candidate, followupSeed.organization)) || (followupSeed.organization && sameScopedName(candidate, followupSeed.organization)) ||
(followupSeed.counterparty && (effectiveFollowupCounterparty &&
!sameScopedName(followupSeed.counterparty, explicitCurrentCounterpartyCandidate) && !sameScopedName(effectiveFollowupCounterparty, explicitCurrentCounterpartyCandidate) &&
sameScopedName(candidate, followupSeed.counterparty)) sameScopedName(candidate, effectiveFollowupCounterparty))
) { ) {
entityCandidates.splice(index, 1); entityCandidates.splice(index, 1);
} }
@ -2000,7 +2027,7 @@ export function buildAssistantMcpDiscoveryTurnInput(
) { ) {
pushReason(reasonCodes, "mcp_discovery_counterparty_from_predecompose"); pushReason(reasonCodes, "mcp_discovery_counterparty_from_predecompose");
} }
if (followupSeed.counterparty && !rawEntitySearchOverridesStaleScope) { if (effectiveFollowupCounterparty && !rawEntitySearchOverridesStaleScope) {
pushReason(reasonCodes, "mcp_discovery_counterparty_from_followup_context"); pushReason(reasonCodes, "mcp_discovery_counterparty_from_followup_context");
} }
if (followupDateScopeApplied) { if (followupDateScopeApplied) {

View File

@ -2007,6 +2007,7 @@ describe("assistant MCP discovery turn input adapter", () => {
previous_discovery_ranking_need: "top_desc", previous_discovery_ranking_need: "top_desc",
previous_filters: { previous_filters: {
organization: orgName, organization: orgName,
counterparty: "\u041f\u0440\u043e\u0432\u0435\u0441\u0442\u0438",
period_from: "2020-01-01", period_from: "2020-01-01",
period_to: "2020-12-31" period_to: "2020-12-31"
} }
@ -2290,4 +2291,47 @@ describe("assistant MCP discovery turn input adapter", () => {
}); });
expect(result.turn_meaning_ref?.explicit_entity_candidates).toBeUndefined(); expect(result.turn_meaning_ref?.explicit_entity_candidates).toBeUndefined();
}); });
it("does not promote organization scope into counterparty subject after a metadata-scoped movement year switch", () => {
const orgName =
"\u041e\u041e\u041e \u0410\u043b\u044c\u0442\u0435\u0440\u043d\u0430\u0442\u0438\u0432\u0430 \u041f\u043b\u044e\u0441";
const result = buildAssistantMcpDiscoveryTurnInput({
userMessage: "\u0417\u0430 2020 \u0433\u043e\u0434.",
followupContext: {
previous_discovery_pilot_scope: "counterparty_movement_evidence_query_movements_v1",
previous_discovery_loop_status: "ready_for_next_hop",
previous_discovery_loop_selected_chain_id: "movement_evidence",
previous_discovery_loop_pending_axes: [],
previous_discovery_loop_asked_domain_family: "movements",
previous_discovery_loop_asked_action_family: "list_movements",
previous_discovery_loop_unsupported_family: "movement_evidence",
previous_discovery_loop_metadata_scope_hint: "\u041d\u0414\u0421",
previous_discovery_loop_subject_resolution_optional: true,
previous_discovery_entity_candidates: ["\u041d\u0414\u0421"],
previous_filters: {
organization: orgName,
counterparty: orgName,
period_to: "2026-04-24"
}
}
});
expect(result.adapter_status).toBe("ready");
expect(result.should_run_discovery).toBe(true);
expect(result.semantic_data_need).toBe("movement evidence");
expect(result.turn_meaning_ref).toMatchObject({
asked_domain_family: "movements",
asked_action_family: "list_movements",
metadata_scope_hint: "\u041d\u0414\u0421",
subject_resolution_optional: true,
explicit_organization_scope: orgName,
explicit_date_scope: "2020",
unsupported_but_understood_family: "movement_evidence",
stale_replay_forbidden: true
});
expect(result.turn_meaning_ref?.explicit_entity_candidates).toBeUndefined();
expect(result.data_need_graph?.subject_candidates).toEqual([]);
expect(result.reason_codes).toContain("mcp_discovery_metadata_scoped_lane_without_subject");
expect(result.reason_codes).not.toContain("mcp_discovery_counterparty_from_followup_context");
});
}); });

View File

@ -1,4 +1,96 @@
[ [
{
"generation_id": "gen-ag04241610-84c8bb",
"created_at": "2026-04-24T16:10:22+00:00",
"mode": "saved_user_sessions",
"title": "AGENT | Post-F cross-stage semantic integrity canary live7",
"count": 24,
"domain": "address_post_f_cross_stage_canary_agent",
"questions": [
"Мне нужно понять, где в 1С по НДС вообще лежат данные. Какие объекты стоит смотреть по НДС?",
"Хорошо, тогда покажи движения по ООО Альтернатива Плюс.",
"За 2020 год.",
"А теперь по документам?",
"А теперь за 2021 год?",
"А теперь за все время?",
"С НДС закончили. Новая тема: покажи документы по Жуковке 51.",
"Хорошо, а теперь платежи по нему тоже покажи.",
"А по нему договоры?",
"А по нему документы?",
"А за 2021?",
"С Жуковкой закончили. Теперь другая задача: быстрый денежный срез по одной организации. Если для ответа нужна организация, просто уточни ее. Сколько вообще входящих денег было за 2020 год?",
"По ООО Альтернатива Плюс.",
"Понял, тогда за все время.",
"Хорошо. А что по ООО Альтернатива Плюс больше в 2020 году: входящие или исходящие деньги?",
"И кто больше всего принес денег этой организации в 2020 году?",
"А в 2021 году?",
"Теперь отдельная тема по конкретному контрагенту. Найди в 1С Группу СВК.",
"Сколько получили по нему за 2020 год?",
"А теперь сколько заплатили?",
"А какое нетто?",
"А по документам?",
"А по движениям?",
"А теперь тот же смысл за 2021 год."
],
"generated_by": "codex_agent",
"saved_case_set_file": "assistant_autogen_saved_user_sessions_20260424161022_gen-ag04241610-84c8bb.json",
"context": {
"llm_provider": null,
"model": null,
"assistant_prompt_version": null,
"decomposition_prompt_version": null,
"prompt_fingerprint": null,
"autogen_personality_id": null,
"autogen_personality_prompt": null,
"source_session_id": null,
"saved_session_file": "assistant_saved_session_20260424161022_gen-ag04241610-84c8bb.json",
"saved_case_set_kind": "agent_semantic_scenario",
"agent_run": true,
"agent_focus": "live7: VAT metadata scoped org/document pivots, ranking year switches, fresh SVK reset",
"architecture_phase": "Post-F Semantic Integrity Hardening",
"source_spec_file": "X:\\1C\\NDC_1C\\docs\\orchestration\\address_truth_harness_post_f_cross_stage_canary_agent_20260424.json",
"scenario_id": "address_truth_harness_post_f_cross_stage_canary_agent_20260424",
"semantic_tags": [
"account_injection_guard",
"all_time_followup",
"bidirectional_value_flow",
"clarification_resume",
"contracts_followup",
"counterparty_pronoun_resolution",
"cross_stage_canary",
"document_lane_continuity",
"document_pivot_after_movement",
"documents_followup",
"documents_pivot",
"grounded_counterparty",
"incoming_value_flow",
"legacy_phase39",
"legacy_phase64",
"legacy_phase67",
"metadata_surface",
"movement_execution",
"movement_lane_after_metadata",
"movements_pivot",
"net_value_flow",
"numeric_counterparty_suffix",
"open_scope_total",
"organization_clarification",
"organization_scope",
"payments_followup",
"payout_value_flow",
"period_clarification_resume",
"post_f",
"repeated_pivot",
"scope_reuse",
"topic_reset",
"value_flow_ranking",
"vat_orientation",
"year_switch",
"year_switch_after_document_pivot",
"year_switch_after_repeated_pivot"
]
}
},
{ {
"generation_id": "gen-ag04241406-abe4d8", "generation_id": "gen-ag04241406-abe4d8",
"created_at": "2026-04-24T14:06:30+00:00", "created_at": "2026-04-24T14:06:30+00:00",

View File

@ -0,0 +1,353 @@
{
"saved_at": "2026-04-24T16:10:22+00:00",
"generation_id": "gen-ag04241610-84c8bb",
"mode": "saved_user_sessions",
"title": "AGENT | Post-F cross-stage semantic integrity canary live7",
"agent_run": true,
"questions": [
"Мне нужно понять, где в 1С по НДС вообще лежат данные. Какие объекты стоит смотреть по НДС?",
"Хорошо, тогда покажи движения по ООО Альтернатива Плюс.",
"За 2020 год.",
"А теперь по документам?",
"А теперь за 2021 год?",
"А теперь за все время?",
"С НДС закончили. Новая тема: покажи документы по Жуковке 51.",
"Хорошо, а теперь платежи по нему тоже покажи.",
"А по нему договоры?",
"А по нему документы?",
"А за 2021?",
"С Жуковкой закончили. Теперь другая задача: быстрый денежный срез по одной организации. Если для ответа нужна организация, просто уточни ее. Сколько вообще входящих денег было за 2020 год?",
"По ООО Альтернатива Плюс.",
"Понял, тогда за все время.",
"Хорошо. А что по ООО Альтернатива Плюс больше в 2020 году: входящие или исходящие деньги?",
"И кто больше всего принес денег этой организации в 2020 году?",
"А в 2021 году?",
"Теперь отдельная тема по конкретному контрагенту. Найди в 1С Группу СВК.",
"Сколько получили по нему за 2020 год?",
"А теперь сколько заплатили?",
"А какое нетто?",
"А по документам?",
"А по движениям?",
"А теперь тот же смысл за 2021 год."
],
"metadata": {
"assistant_prompt_version": null,
"decomposition_prompt_version": null,
"prompt_fingerprint": null,
"agent_focus": "live7: VAT metadata scoped org/document pivots, ranking year switches, fresh SVK reset",
"architecture_phase": "Post-F Semantic Integrity Hardening",
"source_spec_file": "X:\\1C\\NDC_1C\\docs\\orchestration\\address_truth_harness_post_f_cross_stage_canary_agent_20260424.json",
"scenario_id": "address_truth_harness_post_f_cross_stage_canary_agent_20260424",
"semantic_tags": [
"account_injection_guard",
"all_time_followup",
"bidirectional_value_flow",
"clarification_resume",
"contracts_followup",
"counterparty_pronoun_resolution",
"cross_stage_canary",
"document_lane_continuity",
"document_pivot_after_movement",
"documents_followup",
"documents_pivot",
"grounded_counterparty",
"incoming_value_flow",
"legacy_phase39",
"legacy_phase64",
"legacy_phase67",
"metadata_surface",
"movement_execution",
"movement_lane_after_metadata",
"movements_pivot",
"net_value_flow",
"numeric_counterparty_suffix",
"open_scope_total",
"organization_clarification",
"organization_scope",
"payments_followup",
"payout_value_flow",
"period_clarification_resume",
"post_f",
"repeated_pivot",
"scope_reuse",
"topic_reset",
"value_flow_ranking",
"vat_orientation",
"year_switch",
"year_switch_after_document_pivot",
"year_switch_after_repeated_pivot"
]
},
"source_session_id": null,
"session": {
"session_id": null,
"mode": "agent_semantic_run",
"items": [
{
"message_id": "agent-user-001",
"role": "user",
"text": "Мне нужно понять, где в 1С по НДС вообще лежат данные. Какие объекты стоит смотреть по НДС?",
"created_at": "2026-04-24T16:10:22+00:00",
"reply_type": null,
"trace_id": null,
"debug": null
},
{
"message_id": "agent-user-002",
"role": "user",
"text": "Хорошо, тогда покажи движения по ООО Альтернатива Плюс.",
"created_at": "2026-04-24T16:10:22+00:00",
"reply_type": null,
"trace_id": null,
"debug": null
},
{
"message_id": "agent-user-003",
"role": "user",
"text": "За 2020 год.",
"created_at": "2026-04-24T16:10:22+00:00",
"reply_type": null,
"trace_id": null,
"debug": null
},
{
"message_id": "agent-user-004",
"role": "user",
"text": "А теперь по документам?",
"created_at": "2026-04-24T16:10:22+00:00",
"reply_type": null,
"trace_id": null,
"debug": null
},
{
"message_id": "agent-user-005",
"role": "user",
"text": "А теперь за 2021 год?",
"created_at": "2026-04-24T16:10:22+00:00",
"reply_type": null,
"trace_id": null,
"debug": null
},
{
"message_id": "agent-user-006",
"role": "user",
"text": "А теперь за все время?",
"created_at": "2026-04-24T16:10:22+00:00",
"reply_type": null,
"trace_id": null,
"debug": null
},
{
"message_id": "agent-user-007",
"role": "user",
"text": "С НДС закончили. Новая тема: покажи документы по Жуковке 51.",
"created_at": "2026-04-24T16:10:22+00:00",
"reply_type": null,
"trace_id": null,
"debug": null
},
{
"message_id": "agent-user-008",
"role": "user",
"text": "Хорошо, а теперь платежи по нему тоже покажи.",
"created_at": "2026-04-24T16:10:22+00:00",
"reply_type": null,
"trace_id": null,
"debug": null
},
{
"message_id": "agent-user-009",
"role": "user",
"text": "А по нему договоры?",
"created_at": "2026-04-24T16:10:22+00:00",
"reply_type": null,
"trace_id": null,
"debug": null
},
{
"message_id": "agent-user-010",
"role": "user",
"text": "А по нему документы?",
"created_at": "2026-04-24T16:10:22+00:00",
"reply_type": null,
"trace_id": null,
"debug": null
},
{
"message_id": "agent-user-011",
"role": "user",
"text": "А за 2021?",
"created_at": "2026-04-24T16:10:22+00:00",
"reply_type": null,
"trace_id": null,
"debug": null
},
{
"message_id": "agent-user-012",
"role": "user",
"text": "С Жуковкой закончили. Теперь другая задача: быстрый денежный срез по одной организации. Если для ответа нужна организация, просто уточни ее. Сколько вообще входящих денег было за 2020 год?",
"created_at": "2026-04-24T16:10:22+00:00",
"reply_type": null,
"trace_id": null,
"debug": null
},
{
"message_id": "agent-user-013",
"role": "user",
"text": "По ООО Альтернатива Плюс.",
"created_at": "2026-04-24T16:10:22+00:00",
"reply_type": null,
"trace_id": null,
"debug": null
},
{
"message_id": "agent-user-014",
"role": "user",
"text": "Понял, тогда за все время.",
"created_at": "2026-04-24T16:10:22+00:00",
"reply_type": null,
"trace_id": null,
"debug": null
},
{
"message_id": "agent-user-015",
"role": "user",
"text": "Хорошо. А что по ООО Альтернатива Плюс больше в 2020 году: входящие или исходящие деньги?",
"created_at": "2026-04-24T16:10:22+00:00",
"reply_type": null,
"trace_id": null,
"debug": null
},
{
"message_id": "agent-user-016",
"role": "user",
"text": "И кто больше всего принес денег этой организации в 2020 году?",
"created_at": "2026-04-24T16:10:22+00:00",
"reply_type": null,
"trace_id": null,
"debug": null
},
{
"message_id": "agent-user-017",
"role": "user",
"text": "А в 2021 году?",
"created_at": "2026-04-24T16:10:22+00:00",
"reply_type": null,
"trace_id": null,
"debug": null
},
{
"message_id": "agent-user-018",
"role": "user",
"text": "Теперь отдельная тема по конкретному контрагенту. Найди в 1С Группу СВК.",
"created_at": "2026-04-24T16:10:22+00:00",
"reply_type": null,
"trace_id": null,
"debug": null
},
{
"message_id": "agent-user-019",
"role": "user",
"text": "Сколько получили по нему за 2020 год?",
"created_at": "2026-04-24T16:10:22+00:00",
"reply_type": null,
"trace_id": null,
"debug": null
},
{
"message_id": "agent-user-020",
"role": "user",
"text": "А теперь сколько заплатили?",
"created_at": "2026-04-24T16:10:22+00:00",
"reply_type": null,
"trace_id": null,
"debug": null
},
{
"message_id": "agent-user-021",
"role": "user",
"text": "А какое нетто?",
"created_at": "2026-04-24T16:10:22+00:00",
"reply_type": null,
"trace_id": null,
"debug": null
},
{
"message_id": "agent-user-022",
"role": "user",
"text": "А по документам?",
"created_at": "2026-04-24T16:10:22+00:00",
"reply_type": null,
"trace_id": null,
"debug": null
},
{
"message_id": "agent-user-023",
"role": "user",
"text": "А по движениям?",
"created_at": "2026-04-24T16:10:22+00:00",
"reply_type": null,
"trace_id": null,
"debug": null
},
{
"message_id": "agent-user-024",
"role": "user",
"text": "А теперь тот же смысл за 2021 год.",
"created_at": "2026-04-24T16:10:22+00:00",
"reply_type": null,
"trace_id": null,
"debug": null
}
],
"agent_run": true,
"metadata": {
"assistant_prompt_version": null,
"decomposition_prompt_version": null,
"prompt_fingerprint": null,
"agent_focus": "live7: VAT metadata scoped org/document pivots, ranking year switches, fresh SVK reset",
"architecture_phase": "Post-F Semantic Integrity Hardening",
"source_spec_file": "X:\\1C\\NDC_1C\\docs\\orchestration\\address_truth_harness_post_f_cross_stage_canary_agent_20260424.json",
"scenario_id": "address_truth_harness_post_f_cross_stage_canary_agent_20260424",
"semantic_tags": [
"account_injection_guard",
"all_time_followup",
"bidirectional_value_flow",
"clarification_resume",
"contracts_followup",
"counterparty_pronoun_resolution",
"cross_stage_canary",
"document_lane_continuity",
"document_pivot_after_movement",
"documents_followup",
"documents_pivot",
"grounded_counterparty",
"incoming_value_flow",
"legacy_phase39",
"legacy_phase64",
"legacy_phase67",
"metadata_surface",
"movement_execution",
"movement_lane_after_metadata",
"movements_pivot",
"net_value_flow",
"numeric_counterparty_suffix",
"open_scope_total",
"organization_clarification",
"organization_scope",
"payments_followup",
"payout_value_flow",
"period_clarification_resume",
"post_f",
"repeated_pivot",
"scope_reuse",
"topic_reset",
"value_flow_ranking",
"vat_orientation",
"year_switch",
"year_switch_after_document_pivot",
"year_switch_after_repeated_pivot"
]
}
}
}

View File

@ -0,0 +1,97 @@
{
"suite_id": "assistant_saved_session_gen-ag04241610-84c8bb",
"suite_version": "0.1.0",
"schema_version": "assistant_saved_session_suite_v0_1",
"generated_at": "2026-04-24T16:10:22+00:00",
"generation_id": "gen-ag04241610-84c8bb",
"mode": "saved_user_sessions",
"title": "AGENT | Post-F cross-stage semantic integrity canary live7",
"domain": "address_post_f_cross_stage_canary_agent",
"scenario_count": 1,
"case_ids": [
"SAVED-001"
],
"cases": [
{
"case_id": "SAVED-001",
"scenario_tag": "agent_saved_user_sessions",
"title": "AGENT | Post-F cross-stage semantic integrity canary live7",
"question_type": "followup",
"broadness_level": "medium",
"turns": [
{
"user_message": "Мне нужно понять, где в 1С по НДС вообще лежат данные. Какие объекты стоит смотреть по НДС?"
},
{
"user_message": "Хорошо, тогда покажи движения по ООО Альтернатива Плюс."
},
{
"user_message": "За 2020 год."
},
{
"user_message": "А теперь по документам?"
},
{
"user_message": "А теперь за 2021 год?"
},
{
"user_message": "А теперь за все время?"
},
{
"user_message": "С НДС закончили. Новая тема: покажи документы по Жуковке 51."
},
{
"user_message": "Хорошо, а теперь платежи по нему тоже покажи."
},
{
"user_message": "А по нему договоры?"
},
{
"user_message": "А по нему документы?"
},
{
"user_message": "А за 2021?"
},
{
"user_message": "С Жуковкой закончили. Теперь другая задача: быстрый денежный срез по одной организации. Если для ответа нужна организация, просто уточни ее. Сколько вообще входящих денег было за 2020 год?"
},
{
"user_message": "По ООО Альтернатива Плюс."
},
{
"user_message": "Понял, тогда за все время."
},
{
"user_message": "Хорошо. А что по ООО Альтернатива Плюс больше в 2020 году: входящие или исходящие деньги?"
},
{
"user_message": "И кто больше всего принес денег этой организации в 2020 году?"
},
{
"user_message": "А в 2021 году?"
},
{
"user_message": "Теперь отдельная тема по конкретному контрагенту. Найди в 1С Группу СВК."
},
{
"user_message": "Сколько получили по нему за 2020 год?"
},
{
"user_message": "А теперь сколько заплатили?"
},
{
"user_message": "А какое нетто?"
},
{
"user_message": "А по документам?"
},
{
"user_message": "А по движениям?"
},
{
"user_message": "А теперь тот же смысл за 2021 год."
}
]
}
]
}