518 lines
20 KiB
JSON
518 lines
20 KiB
JSON
{
|
||
"schema_version": "agent_issue_catalog_v1",
|
||
"updated_at": "2026-05-24",
|
||
"principles": [
|
||
"No accepted save without effective_runtime.json.",
|
||
"No auto-coder without allowed issue_code, root_layers, allowed/forbidden targets, rerun_matrix, and answer contract.",
|
||
"Subagents are read-only reviewers; Lead/Orchestrator owns final verdict and repair decision."
|
||
],
|
||
"issues": {
|
||
"runtime_manifest_missing": {
|
||
"severity": "P0",
|
||
"business_meaning": "Прогон нельзя воспроизвести, поэтому audit/repair/acceptance нельзя считать доказанными.",
|
||
"root_layers": ["run_reproducibility"],
|
||
"detectors": ["missing_effective_runtime_json"],
|
||
"allowed_patch_targets": [
|
||
"scripts/domain_case_loop.py",
|
||
"scripts/domain_truth_harness.py",
|
||
"scripts/stage_agent_loop.py",
|
||
"scripts/save_agent_semantic_run.py",
|
||
"scripts/agent_runtime_manifest.py"
|
||
],
|
||
"forbidden_patch_targets": [
|
||
"business routing",
|
||
"MCP runtime",
|
||
"domain contracts"
|
||
],
|
||
"rerun_matrix": [
|
||
"same_spec_direct_runner",
|
||
"same_spec_stage_runner",
|
||
"accepted_smoke_pack"
|
||
],
|
||
"acceptance": {
|
||
"must_have": [
|
||
"effective_runtime.json",
|
||
"git_sha",
|
||
"runner",
|
||
"llm_model",
|
||
"temperature",
|
||
"prompt_source",
|
||
"prompt_hash"
|
||
]
|
||
}
|
||
},
|
||
"prompt_registry_opaque": {
|
||
"severity": "P0",
|
||
"business_meaning": "Неясно, какой normalizer prompt реально исполняется.",
|
||
"root_layers": ["prompt_registry", "runtime_config"],
|
||
"detectors": [
|
||
"default_prompt_version_missing_files",
|
||
"silent_prompt_fallback",
|
||
"preset_version_mismatch"
|
||
],
|
||
"allowed_patch_targets": [
|
||
"llm_normalizer/backend/src/services/promptBuilder.ts",
|
||
"llm_normalizer/backend/src/config.ts",
|
||
"llm_normalizer/data/presets/*.json",
|
||
"scripts/prompt_registry_healthcheck.py",
|
||
"scripts/agent_runtime_manifest.py"
|
||
],
|
||
"forbidden_patch_targets": [
|
||
"domain routing rewrites",
|
||
"business answer heuristics"
|
||
],
|
||
"rerun_matrix": [
|
||
"prompt_healthcheck",
|
||
"normalizer_smoke",
|
||
"accepted_smoke_pack"
|
||
]
|
||
},
|
||
"margin_domain_leak_accounting_route": {
|
||
"severity": "P0",
|
||
"business_meaning": "Запрос про маржинальность номенклатуры ушёл в чужой бухгалтерский домен вместо выручки, себестоимости, валовой прибыли и процента маржи.",
|
||
"root_layers": ["intent", "route", "capability", "domain_contract"],
|
||
"expected_answer_contract": "margin_profitability_v1",
|
||
"detectors": [
|
||
"forbidden_margin_terms",
|
||
"missing_revenue_cogs_margin_fields",
|
||
"margin_payment_document_false_source",
|
||
"wrong_capability_family"
|
||
],
|
||
"allowed_patch_targets": [
|
||
"llm_normalizer/backend/src/services/addressQueryClassifier.ts",
|
||
"llm_normalizer/backend/src/services/addressInventoryIntentSignals.ts",
|
||
"llm_normalizer/backend/src/services/addressIntentResolver.ts",
|
||
"llm_normalizer/backend/src/services/addressCapabilityPolicy.ts",
|
||
"llm_normalizer/backend/src/services/addressFilterExtractor.ts",
|
||
"llm_normalizer/backend/src/services/address_runtime/**",
|
||
"docs/orchestration/contracts/margin_profitability_v1.json"
|
||
],
|
||
"forbidden_patch_targets": [
|
||
"fake data",
|
||
"silent heuristic masking",
|
||
"global orchestration rewrite",
|
||
"MCP protocol rewrite"
|
||
],
|
||
"rerun_matrix": [
|
||
"failed_margin_scenario",
|
||
"margin_neighbor_pack",
|
||
"wrong_domain_trap_pack",
|
||
"selected_object_followup_pack",
|
||
"accepted_smoke_pack"
|
||
],
|
||
"acceptance": {
|
||
"must_have": [
|
||
"direct_answer_first",
|
||
"period",
|
||
"revenue_ex_vat_or_honest_unknown",
|
||
"cogs_or_honest_unknown",
|
||
"gross_profit_or_honest_unknown",
|
||
"margin_pct_or_honest_unknown",
|
||
"next_action_if_limited"
|
||
],
|
||
"must_not_have": [
|
||
"route ids",
|
||
"debug ids",
|
||
"fixed assets leak",
|
||
"amortization leak",
|
||
"payment document as margin source"
|
||
]
|
||
}
|
||
},
|
||
"business_direct_answer_missing": {
|
||
"severity": "P0",
|
||
"business_meaning": "Ответ не начинает с прямого бизнес-вывода, хотя пользователь задал прямой вопрос.",
|
||
"root_layers": ["answer_surface", "business_utility"],
|
||
"expected_answer_contract": "direct_answer_surface_v1",
|
||
"detectors": ["first_line_not_direct_answer", "top_level_scaffold_before_answer"],
|
||
"allowed_patch_targets": [
|
||
"llm_normalizer/backend/src/services/address_runtime/composeStage.ts",
|
||
"llm_normalizer/backend/src/services/assistantMcpDiscoveryResponseCandidate.ts",
|
||
"llm_normalizer/backend/src/services/assistantService.ts"
|
||
],
|
||
"forbidden_patch_targets": [
|
||
"routing rewrites",
|
||
"fake evidence",
|
||
"global runtime rewrite"
|
||
],
|
||
"rerun_matrix": [
|
||
"failed_scenario",
|
||
"direct_answer_surface_pack",
|
||
"accepted_smoke_pack"
|
||
],
|
||
"acceptance": {
|
||
"must_have": [
|
||
"direct_answer_first",
|
||
"business_meaning_before_service_context",
|
||
"honest_unknown_when_evidence_is_limited"
|
||
],
|
||
"must_not_have": [
|
||
"debug ids before answer",
|
||
"route ids before answer",
|
||
"service scaffold before answer"
|
||
]
|
||
}
|
||
},
|
||
"technical_garbage_in_answer": {
|
||
"severity": "P0",
|
||
"business_meaning": "Финальный ответ протащил debug/runtime/MCP-механику в пользовательскую поверхность.",
|
||
"root_layers": ["answer_surface", "business_utility"],
|
||
"expected_answer_contract": "technical_garbage_free_answer_v1",
|
||
"detectors": ["runtime_tokens_in_user_answer", "capability_ids_in_user_answer"],
|
||
"allowed_patch_targets": [
|
||
"llm_normalizer/backend/src/services/address_runtime/composeStage.ts",
|
||
"llm_normalizer/backend/src/services/assistantMcpDiscoveryResponseCandidate.ts",
|
||
"llm_normalizer/backend/src/services/assistantService.ts"
|
||
],
|
||
"forbidden_patch_targets": [
|
||
"route masking",
|
||
"debug deletion from artifacts"
|
||
],
|
||
"rerun_matrix": [
|
||
"failed_scenario",
|
||
"technical_garbage_canary_pack",
|
||
"accepted_smoke_pack"
|
||
],
|
||
"acceptance": {
|
||
"must_have": [
|
||
"business_answer_only",
|
||
"evidence_summary_when_useful"
|
||
],
|
||
"must_not_have": [
|
||
"route ids",
|
||
"capability ids",
|
||
"debug payload",
|
||
"raw runtime enums"
|
||
]
|
||
}
|
||
},
|
||
"accounting_contract_missing": {
|
||
"severity": "P1",
|
||
"business_meaning": "Ответ не раскрыл обязательный бухгалтерский/доказательный контракт для запрошенного расчёта.",
|
||
"root_layers": ["domain_contract", "answer_surface", "evidence"],
|
||
"detectors": ["required_contract_fields_missing"],
|
||
"allowed_patch_targets": [
|
||
"docs/orchestration/contracts/*.json",
|
||
"llm_normalizer/backend/src/services/address_runtime/composeStage.ts",
|
||
"llm_normalizer/backend/src/services/assistantMcpDiscoveryResponseCandidate.ts"
|
||
],
|
||
"forbidden_patch_targets": [
|
||
"fake data",
|
||
"silent heuristic masking"
|
||
],
|
||
"rerun_matrix": [
|
||
"failed_scenario",
|
||
"contract_field_pack",
|
||
"accepted_smoke_pack"
|
||
]
|
||
},
|
||
"business_next_step_missing": {
|
||
"severity": "P2",
|
||
"business_meaning": "Ограниченный ответ не предлагает полезный следующий шаг.",
|
||
"root_layers": ["answer_surface", "business_utility"],
|
||
"expected_answer_contract": "limited_answer_next_action_v1",
|
||
"detectors": ["limited_answer_without_next_action"],
|
||
"allowed_patch_targets": [
|
||
"llm_normalizer/backend/src/services/address_runtime/composeStage.ts",
|
||
"llm_normalizer/backend/src/services/assistantMcpDiscoveryResponseCandidate.ts"
|
||
],
|
||
"forbidden_patch_targets": [
|
||
"evidence fabrication",
|
||
"route masking"
|
||
],
|
||
"rerun_matrix": [
|
||
"failed_scenario",
|
||
"limited_answer_pack",
|
||
"accepted_smoke_pack"
|
||
],
|
||
"acceptance": {
|
||
"must_have": [
|
||
"clear_limitation",
|
||
"next_action_if_limited",
|
||
"no_overclaiming"
|
||
],
|
||
"must_not_have": [
|
||
"fake evidence",
|
||
"masked route failure"
|
||
]
|
||
}
|
||
},
|
||
"business_utility_gap": {
|
||
"severity": "P2",
|
||
"business_meaning": "Ответ технически заземлен, но для узкого бизнес follow-up остается слишком тяжелым или недостаточно практичным.",
|
||
"root_layers": ["answer_surface", "business_utility"],
|
||
"expected_answer_contract": "compact_business_followup_surface_v1",
|
||
"detectors": [
|
||
"business_answer_too_verbose_output",
|
||
"top_level_scaffold_before_answer",
|
||
"runtime_tokens_in_user_answer",
|
||
"capability_ids_in_user_answer",
|
||
"limited_answer_without_next_action"
|
||
],
|
||
"allowed_patch_targets": [
|
||
"llm_normalizer/backend/src/services/address_runtime/composeStage.ts",
|
||
"llm_normalizer/backend/src/services/address_runtime/inventoryReplyBuilders.ts",
|
||
"llm_normalizer/backend/src/services/assistantMcpDiscoveryResponseCandidate.ts",
|
||
"llm_normalizer/backend/src/services/assistantService.ts"
|
||
],
|
||
"forbidden_patch_targets": [
|
||
"route masking",
|
||
"fake evidence",
|
||
"evidence deletion from artifacts"
|
||
],
|
||
"rerun_matrix": [
|
||
"failed_scenario",
|
||
"answer_surface_pack",
|
||
"accepted_smoke_pack"
|
||
],
|
||
"acceptance": {
|
||
"must_have": [
|
||
"direct_answer_first",
|
||
"compact_business_surface",
|
||
"short_evidence_basis",
|
||
"honest_limitation_when_needed"
|
||
],
|
||
"must_not_have": [
|
||
"verbose proof packet by default",
|
||
"technical ids",
|
||
"duplicated answer blocks"
|
||
]
|
||
}
|
||
},
|
||
"route_candidate_enablement_gap": {
|
||
"severity": "P1",
|
||
"business_meaning": "Планировщик понял бизнес-запрос, но route candidate ещё не исполняется как точная возможность.",
|
||
"root_layers": ["route_candidate", "runtime_capability"],
|
||
"detectors": ["route_candidate_needs_enablement"],
|
||
"allowed_patch_targets": [
|
||
"llm_normalizer/backend/src/services/assistantMcpDiscoveryRuntimeBridge.ts",
|
||
"llm_normalizer/backend/src/services/assistantMcpDiscoveryPlanner.ts",
|
||
"llm_normalizer/backend/src/services/assistantMcpDiscoveryPilotExecutor.ts",
|
||
"llm_normalizer/backend/src/services/addressRecipeCatalog.ts"
|
||
],
|
||
"forbidden_patch_targets": [
|
||
"global orchestration rewrite",
|
||
"fake fixtures"
|
||
],
|
||
"rerun_matrix": [
|
||
"failed_scenario",
|
||
"route_candidate_pack",
|
||
"accepted_smoke_pack"
|
||
]
|
||
},
|
||
"backend_error_response": {
|
||
"severity": "P0",
|
||
"business_meaning": "Пользовательский ответ сорвался во внутреннюю runtime/LLM ошибку вместо полезного бизнес-ответа или честной границы.",
|
||
"root_layers": ["runtime_error", "business_utility"],
|
||
"detectors": ["backend_error_response_signal"],
|
||
"allowed_patch_targets": [
|
||
"llm_normalizer/backend/src/services/assistantLivingChatRuntimeAdapter.ts",
|
||
"llm_normalizer/backend/src/services/assistantLivingChatHandlerRuntimeAdapter.ts",
|
||
"llm_normalizer/backend/src/services/assistantService.ts",
|
||
"scripts/review_assistant_stage1_run.py"
|
||
],
|
||
"forbidden_patch_targets": [
|
||
"fake success fallback",
|
||
"business routing rewrites",
|
||
"MCP protocol rewrite"
|
||
],
|
||
"rerun_matrix": [
|
||
"failed_gui_turn",
|
||
"same_saved_session_replay",
|
||
"big_gui_pack",
|
||
"accepted_smoke_pack"
|
||
],
|
||
"acceptance": {
|
||
"must_have": [
|
||
"no backend_error reply_type",
|
||
"user-facing answer or honest boundary",
|
||
"trace remains diagnosable"
|
||
],
|
||
"must_not_have": [
|
||
"internal error text",
|
||
"empty assistant answer",
|
||
"masked fake business fact"
|
||
]
|
||
}
|
||
},
|
||
"year_ranking_direct_answer_missing": {
|
||
"severity": "P0",
|
||
"business_meaning": "Вопрос про самый доходный/лучший год не получает прямой годовой ranking-ответ в первой строке.",
|
||
"root_layers": ["answer_shape_mismatch", "business_utility", "ranking_answer_surface"],
|
||
"detectors": ["top_year_direct_answer_missing"],
|
||
"allowed_patch_targets": [
|
||
"llm_normalizer/backend/src/services/assistantMcpDiscoveryResponseCandidate.ts",
|
||
"llm_normalizer/backend/src/services/assistantMcpDiscoveryRuntimeBridge.ts",
|
||
"llm_normalizer/backend/src/services/assistantMcpDiscoveryTurnInputAdapter.ts",
|
||
"scripts/review_assistant_stage1_run.py"
|
||
],
|
||
"forbidden_patch_targets": [
|
||
"fake revenue totals",
|
||
"global business overview rewrite",
|
||
"silent route masking"
|
||
],
|
||
"rerun_matrix": [
|
||
"failed_top_year_turn",
|
||
"business_overview_counterparty_pack",
|
||
"big_gui_pack",
|
||
"accepted_smoke_pack"
|
||
],
|
||
"acceptance": {
|
||
"must_have": [
|
||
"direct winning year in first answer line",
|
||
"basis of ranking",
|
||
"amount or honest limitation",
|
||
"profit-vs-cashflow limitation if needed"
|
||
],
|
||
"must_not_have": [
|
||
"generic business overview first",
|
||
"route ids",
|
||
"unqualified чистая прибыль claim"
|
||
]
|
||
}
|
||
},
|
||
"purchase_date_vat_anchor_ambiguous": {
|
||
"severity": "P1",
|
||
"business_meaning": "Ответ по НДС на дату покупки считает налоговый период, но не объясняет, как дата покупки стала якорем периода.",
|
||
"root_layers": ["domain_anchor_gap", "field_mapping_gap", "answer_surface"],
|
||
"detectors": ["vat_purchase_date_anchor_missing"],
|
||
"allowed_patch_targets": [
|
||
"llm_normalizer/backend/src/services/address_runtime/composeStage.ts",
|
||
"llm_normalizer/backend/src/services/addressFilterExtractor.ts",
|
||
"llm_normalizer/backend/src/services/assistantTransitionPolicy.ts",
|
||
"scripts/review_assistant_stage1_run.py"
|
||
],
|
||
"forbidden_patch_targets": [
|
||
"fake purchase date",
|
||
"VAT route rewrite without evidence",
|
||
"MCP protocol rewrite"
|
||
],
|
||
"rerun_matrix": [
|
||
"failed_vat_purchase_date_turn",
|
||
"vat_tax_period_pack",
|
||
"selected_object_followup_pack",
|
||
"accepted_smoke_pack"
|
||
],
|
||
"acceptance": {
|
||
"must_have": [
|
||
"purchase date anchor explanation",
|
||
"tax period window",
|
||
"sales/purchase book basis",
|
||
"honest unknown if exact purchase date is inferred"
|
||
],
|
||
"must_not_have": [
|
||
"unexplained period jump",
|
||
"fake document source",
|
||
"forecast presented as confirmed declaration"
|
||
]
|
||
}
|
||
},
|
||
"counterparty_value_flow_misrouted_to_company_profit": {
|
||
"severity": "P0",
|
||
"business_meaning": "Контрагентский вопрос про получили/заплатили/нетто отвечен company-level прибылью или 90/91/99 вместо контрагентского денежного потока.",
|
||
"root_layers": ["followup_action_resolution_gap", "answer_shape_mismatch", "domain_scope_leak"],
|
||
"detectors": [
|
||
"counterparty_value_flow_required_surface",
|
||
"counterparty_value_flow_profit_accounts_forbidden",
|
||
"counterparty_value_flow_profit_mismatch"
|
||
],
|
||
"allowed_patch_targets": [
|
||
"llm_normalizer/backend/src/services/assistantMcpDiscoveryResponseCandidate.ts",
|
||
"llm_normalizer/backend/src/services/assistantMcpDiscoveryRuntimeBridge.ts",
|
||
"llm_normalizer/backend/src/services/assistantMcpDiscoveryTurnInputAdapter.ts",
|
||
"llm_normalizer/backend/src/services/assistantTransitionPolicy.ts",
|
||
"scripts/review_assistant_stage1_run.py"
|
||
],
|
||
"forbidden_patch_targets": [
|
||
"fake counterparty totals",
|
||
"company profit fallback",
|
||
"global orchestration rewrite"
|
||
],
|
||
"rerun_matrix": [
|
||
"failed_counterparty_value_flow_turn",
|
||
"business_overview_counterparty_pack",
|
||
"cross_scope_followup_pack",
|
||
"accepted_smoke_pack"
|
||
],
|
||
"acceptance": {
|
||
"must_have": [
|
||
"counterparty scope",
|
||
"received amount or honest unknown",
|
||
"paid amount or honest unknown",
|
||
"net amount or honest unknown",
|
||
"company-profit boundary"
|
||
],
|
||
"must_not_have": [
|
||
"90/91/99 as direct counterparty answer",
|
||
"company profit as counterparty net",
|
||
"wrong selected entity"
|
||
]
|
||
}
|
||
},
|
||
"answer_layering_noise": {
|
||
"severity": "P1",
|
||
"business_meaning": "Ответ открывается лишним слоем рассуждений/рамок вместо короткого прямого вывода.",
|
||
"root_layers": ["answer_surface", "business_utility"],
|
||
"detectors": ["answer_layering_noise_signal"],
|
||
"allowed_patch_targets": [
|
||
"llm_normalizer/backend/src/services/address_runtime/composeStage.ts",
|
||
"llm_normalizer/backend/src/services/assistantMcpDiscoveryResponseCandidate.ts",
|
||
"scripts/review_assistant_stage1_run.py"
|
||
],
|
||
"forbidden_patch_targets": [
|
||
"fake evidence",
|
||
"route masking"
|
||
],
|
||
"rerun_matrix": [
|
||
"failed_gui_turn",
|
||
"direct_answer_surface_pack",
|
||
"accepted_smoke_pack"
|
||
]
|
||
},
|
||
"business_answer_too_verbose": {
|
||
"severity": "P1",
|
||
"business_meaning": "Ответ слишком длинный для прямого бизнес-вопроса и снижает практическую полезность.",
|
||
"root_layers": ["answer_surface", "business_utility"],
|
||
"detectors": ["business_answer_too_verbose_output", "business_answer_too_verbose_signal"],
|
||
"allowed_patch_targets": [
|
||
"llm_normalizer/backend/src/services/address_runtime/composeStage.ts",
|
||
"llm_normalizer/backend/src/services/assistantMcpDiscoveryResponseCandidate.ts",
|
||
"scripts/review_assistant_stage1_run.py"
|
||
],
|
||
"forbidden_patch_targets": [
|
||
"evidence deletion from artifacts",
|
||
"business fact removal"
|
||
],
|
||
"rerun_matrix": [
|
||
"failed_gui_turn",
|
||
"direct_answer_surface_pack",
|
||
"accepted_smoke_pack"
|
||
]
|
||
},
|
||
"bank_counterparty_misclassified_as_business_partner": {
|
||
"severity": "P1",
|
||
"business_meaning": "Банк/финансовая организация ошибочно подана как обычный клиент/поставщик без роли, назначения платежа и договорного контекста.",
|
||
"root_layers": ["business_semantic_role_gap", "domain_scope"],
|
||
"detectors": [
|
||
"bank_counterparty_boundary_required",
|
||
"bank_counterparty_role_boundary_missing"
|
||
],
|
||
"allowed_patch_targets": [
|
||
"llm_normalizer/backend/src/services/address_runtime/composeStage.ts",
|
||
"llm_normalizer/backend/src/services/assistantMcpDiscoveryResponseCandidate.ts",
|
||
"scripts/review_assistant_stage1_run.py"
|
||
],
|
||
"forbidden_patch_targets": [
|
||
"hard-coded bank blacklist as final truth",
|
||
"fake operation purpose",
|
||
"global routing rewrite"
|
||
],
|
||
"rerun_matrix": [
|
||
"failed_bank_counterparty_turn",
|
||
"counterparty_value_pack",
|
||
"accepted_smoke_pack"
|
||
]
|
||
}
|
||
}
|
||
}
|