205 lines
9.7 KiB
JSON
205 lines
9.7 KiB
JSON
{
|
||
"schema_version": "agent_detector_registry_v1",
|
||
"updated_at": "2026-05-24",
|
||
"purpose": "Machine-readable registry for detector names referenced by issue_catalog and business answer contracts. This keeps manual semantic findings on the path to repeatable replay/eval checks.",
|
||
"detectors": {
|
||
"missing_effective_runtime_json": {
|
||
"kind": "artifact_presence",
|
||
"automation_level": "automatic",
|
||
"description": "Run artifact directory must contain effective_runtime.json before a replay can be accepted or saved as evidence.",
|
||
"issue_codes": ["runtime_manifest_missing"],
|
||
"inputs": ["artifacts/domain_runs/<run_id>/effective_runtime.json"],
|
||
"check": {
|
||
"required_files": ["effective_runtime.json"]
|
||
}
|
||
},
|
||
"default_prompt_version_missing_files": {
|
||
"kind": "prompt_registry_healthcheck",
|
||
"automation_level": "automatic",
|
||
"description": "DEFAULT_PROMPT_VERSION points to prompt files that are missing from the prompt registry.",
|
||
"issue_codes": ["prompt_registry_opaque"],
|
||
"inputs": ["llm_normalizer/backend/src/services/promptBuilder.ts", "llm_normalizer/data/presets/*.json"],
|
||
"check": {
|
||
"command": "python scripts/prompt_registry_healthcheck.py"
|
||
}
|
||
},
|
||
"silent_prompt_fallback": {
|
||
"kind": "prompt_registry_healthcheck",
|
||
"automation_level": "automatic",
|
||
"description": "Runtime used a fallback prompt without explicit source/hash metadata in artifacts.",
|
||
"issue_codes": ["prompt_registry_opaque"],
|
||
"inputs": ["effective_runtime.json", "prompt registry"],
|
||
"check": {
|
||
"manifest_fields": ["prompt_source", "prompt_hash"],
|
||
"forbidden_prompt_sources": ["fallback", "unknown"]
|
||
}
|
||
},
|
||
"preset_version_mismatch": {
|
||
"kind": "prompt_registry_healthcheck",
|
||
"automation_level": "automatic",
|
||
"description": "Active preset prompt version does not match the runtime prompt version used by the replay.",
|
||
"issue_codes": ["prompt_registry_opaque"],
|
||
"inputs": ["shared_llm_connection.json", "effective_runtime.json", "prompt presets"],
|
||
"check": {
|
||
"compare_fields": ["prompt_version"]
|
||
}
|
||
},
|
||
"forbidden_margin_terms": {
|
||
"kind": "answer_text_regex_forbidden",
|
||
"automation_level": "automatic",
|
||
"description": "Margin answer contains wrong-domain terms such as fixed assets, amortization, bank, payment, or settlement vocabulary.",
|
||
"issue_codes": ["margin_domain_leak_accounting_route"],
|
||
"inputs": ["steps/<step_id>/output.md", "steps/<step_id>/turn.json"],
|
||
"check": {
|
||
"forbidden_patterns": [
|
||
"(?i)(амортизац|объект\\s+ОС|основн(ые|ых)?\\s+средств|payment_document|settlement|банк|оплат[аы])"
|
||
]
|
||
}
|
||
},
|
||
"missing_revenue_cogs_margin_fields": {
|
||
"kind": "answer_text_required_any",
|
||
"automation_level": "semi_automatic",
|
||
"description": "Margin answer does not mention revenue, COGS/cost, gross profit, margin, or an honest limitation around those fields.",
|
||
"issue_codes": ["margin_domain_leak_accounting_route"],
|
||
"inputs": ["steps/<step_id>/output.md"],
|
||
"check": {
|
||
"required_patterns_any": [
|
||
"(?i)(выруч|себестоим|валов|марж|не хватает|не могу подтвердить|не подтвержден|unknown)"
|
||
]
|
||
}
|
||
},
|
||
"wrong_capability_family": {
|
||
"kind": "trace_value_guard",
|
||
"automation_level": "semi_automatic",
|
||
"description": "Trace/capability family for a margin question points to another accounting family instead of margin/profitability/inventory evidence.",
|
||
"issue_codes": ["margin_domain_leak_accounting_route"],
|
||
"inputs": ["steps/<step_id>/turn.json"],
|
||
"check": {
|
||
"forbidden_trace_markers": ["fixed_asset", "amortization", "payment_document", "settlement"]
|
||
}
|
||
},
|
||
"margin_domain_leak_accounting_route": {
|
||
"kind": "composite_detector",
|
||
"automation_level": "semi_automatic",
|
||
"description": "Composite margin-domain detector used by margin_profitability_v1 contract to group wrong-domain route leaks.",
|
||
"issue_codes": ["margin_domain_leak_accounting_route"],
|
||
"inputs": ["steps/<step_id>/output.md", "steps/<step_id>/turn.json"],
|
||
"check": {
|
||
"uses_detectors": ["forbidden_margin_terms", "wrong_capability_family"]
|
||
}
|
||
},
|
||
"margin_required_fields_missing": {
|
||
"kind": "contract_field_detector",
|
||
"automation_level": "semi_automatic",
|
||
"description": "Margin answer misses required revenue/COGS/gross profit/margin fields or honest unknowns.",
|
||
"issue_codes": ["margin_domain_leak_accounting_route"],
|
||
"inputs": ["steps/<step_id>/output.md", "docs/orchestration/contracts/margin_profitability_v1.json"],
|
||
"check": {
|
||
"uses_detectors": ["missing_revenue_cogs_margin_fields"]
|
||
}
|
||
},
|
||
"margin_next_action_missing": {
|
||
"kind": "limited_answer_next_action",
|
||
"automation_level": "semi_automatic",
|
||
"description": "Limited margin answer does not propose the next verifiable action.",
|
||
"issue_codes": ["business_next_step_missing", "margin_domain_leak_accounting_route"],
|
||
"inputs": ["steps/<step_id>/output.md"],
|
||
"check": {
|
||
"uses_detectors": ["limited_answer_without_next_action"]
|
||
}
|
||
},
|
||
"margin_payment_document_false_source": {
|
||
"kind": "answer_text_regex_forbidden",
|
||
"automation_level": "automatic",
|
||
"description": "Margin answer treats payment/bank documents as the source for margin calculation.",
|
||
"issue_codes": ["margin_domain_leak_accounting_route"],
|
||
"inputs": ["steps/<step_id>/output.md"],
|
||
"check": {
|
||
"forbidden_patterns": ["(?i)(payment_document|банковск|плат[её]ж|оплат[аы]).{0,80}(марж|себестоим|валов)"]
|
||
}
|
||
},
|
||
"margin_os_amortization_leak": {
|
||
"kind": "answer_text_regex_forbidden",
|
||
"automation_level": "automatic",
|
||
"description": "Margin answer leaks fixed-assets or amortization language.",
|
||
"issue_codes": ["margin_domain_leak_accounting_route"],
|
||
"inputs": ["steps/<step_id>/output.md"],
|
||
"check": {
|
||
"forbidden_patterns": ["(?i)(амортизац|объект\\s+ОС|основн(ые|ых)?\\s+средств)"]
|
||
}
|
||
},
|
||
"first_line_not_direct_answer": {
|
||
"kind": "answer_text_shape",
|
||
"automation_level": "semi_automatic",
|
||
"description": "The first meaningful line is not a direct business answer for a direct user question.",
|
||
"issue_codes": ["business_direct_answer_missing"],
|
||
"inputs": ["steps/<step_id>/output.md"],
|
||
"check": {
|
||
"first_line_should_be": "business_answer_or_honest_boundary"
|
||
}
|
||
},
|
||
"top_level_scaffold_before_answer": {
|
||
"kind": "answer_text_regex_forbidden_in_prefix",
|
||
"automation_level": "automatic",
|
||
"description": "Answer starts with scaffold/service narration before the user-facing business conclusion.",
|
||
"issue_codes": ["business_direct_answer_missing"],
|
||
"inputs": ["steps/<step_id>/output.md"],
|
||
"check": {
|
||
"prefix_line_count": 3,
|
||
"forbidden_patterns": ["(?i)(для ответа|сначала|я проверю|я посмотрю|route|debug|capability)"]
|
||
}
|
||
},
|
||
"runtime_tokens_in_user_answer": {
|
||
"kind": "answer_text_regex_forbidden",
|
||
"automation_level": "automatic",
|
||
"description": "Final user-facing answer contains runtime/debug/service tokens.",
|
||
"issue_codes": ["technical_garbage_in_answer"],
|
||
"inputs": ["steps/<step_id>/output.md"],
|
||
"check": {
|
||
"forbidden_patterns": ["(?i)(route_id|capability_id|runtime_|snapshot_items|debug|answer_object|selected_object)"]
|
||
}
|
||
},
|
||
"capability_ids_in_user_answer": {
|
||
"kind": "answer_text_regex_forbidden",
|
||
"automation_level": "automatic",
|
||
"description": "Final user-facing answer contains capability ids or route ids.",
|
||
"issue_codes": ["technical_garbage_in_answer"],
|
||
"inputs": ["steps/<step_id>/output.md"],
|
||
"check": {
|
||
"forbidden_patterns": ["(?i)(capability[_ -]?id|route[_ -]?id|address\\.[a-z0-9_\\.]+)"]
|
||
}
|
||
},
|
||
"required_contract_fields_missing": {
|
||
"kind": "contract_field_detector",
|
||
"automation_level": "semi_automatic",
|
||
"description": "Answer does not satisfy required fields from the expected business answer contract.",
|
||
"issue_codes": ["accounting_contract_missing"],
|
||
"inputs": ["steps/<step_id>/output.md", "docs/orchestration/contracts/<contract_id>.json"],
|
||
"check": {
|
||
"contract_field": "answer_surface.required_fields"
|
||
}
|
||
},
|
||
"limited_answer_without_next_action": {
|
||
"kind": "answer_text_required_when_limited",
|
||
"automation_level": "semi_automatic",
|
||
"description": "Answer states a limitation but gives no concrete next action for recovering or narrowing the answer.",
|
||
"issue_codes": ["business_next_step_missing"],
|
||
"inputs": ["steps/<step_id>/output.md"],
|
||
"check": {
|
||
"limited_patterns": ["(?i)(не могу|не хватает|не подтвержден|нет данных|недостаточно)"],
|
||
"required_next_action_patterns_any": ["(?i)(можно|следующ|уточн|перезапусти|проверь|выбери|нужен|добавь)"]
|
||
}
|
||
},
|
||
"route_candidate_needs_enablement": {
|
||
"kind": "stage_review_signal",
|
||
"automation_level": "semi_automatic",
|
||
"description": "Stage review produced a route candidate that still needs runtime capability enablement.",
|
||
"issue_codes": ["route_candidate_enablement_gap"],
|
||
"inputs": ["run_review.json", "repair_targets.json"],
|
||
"check": {
|
||
"target_status": "needs_route_enablement"
|
||
}
|
||
}
|
||
}
|
||
}
|