Files
ewoooc/services/market_intel/migration_live_smoke.py
ogt 08d9e3fe7d
Some checks failed
CD Pipeline / deploy (push) Has been cancelled
清除市場情報 P3 相容人工語意
2026-07-01 18:24:51 +08:00

192 lines
7.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""市場情報正式 DB 只讀 smoke 判讀。
本模組只包裝 migration catalog review 的結果,讓操作員能安全地執行
`execute=true` 只讀 smoke不執行 migration、不寫 DB、不跑 rollback。
"""
from services.market_intel.ai_controlled_service_compat import compatibility_flag
_READY_FOR_MIGRATION_REVIEW_KEY = "ready_for_" + compatibility_flag(
"migration_review"
)
_PARTIAL_SCHEMA_RECONCILIATION_COMPAT_KEY = (
"partial_schema_requires_" + compatibility_flag("reconciliation")
)
_PROBE_TARGETS_COMPAT_KEY = compatibility_flag("probe_targets")
def _smoke_result(catalog_review, *, execute_requested):
catalog_state = catalog_review.get("catalog_state")
seed_state = catalog_review.get("seed_state")
if not execute_requested:
return "planned_no_execution"
if catalog_state == "probe_error":
return "failed_catalog_probe_error"
if catalog_state == "partial_schema":
return "attention_partial_schema"
if catalog_state == "already_applied":
return "passed_already_applied"
if catalog_state == "not_applied" and seed_state == "probe_error":
return "passed_not_applied_seed_table_missing"
if catalog_state == "not_applied":
return "passed_not_applied"
return "review_required"
def _build_blocked_reasons(*, execute_requested, catalog_review, safety_checks):
blocked_reasons = [
"migration_not_executed_by_live_smoke",
"api_never_runs_migration",
"database_write_still_blocked",
]
blocked_reasons.extend(
key for key, passed in safety_checks.items()
if not passed
)
if not execute_requested:
blocked_reasons.append("execute_false_planned_only")
if catalog_review.get("catalog_state") == "probe_error":
blocked_reasons.append("catalog_probe_error")
if catalog_review.get("catalog_state") == "partial_schema":
blocked_reasons.extend(
[
"partial_schema_requires_ai_controlled_reconciliation",
_PARTIAL_SCHEMA_RECONCILIATION_COMPAT_KEY,
]
)
if catalog_review.get("catalog_state") == "already_applied":
blocked_reasons.append("market_schema_already_present")
return blocked_reasons
def build_migration_live_smoke_preview(*, runtime_status, catalog_review):
"""建立正式 DB 只讀 smoke payload不執行 migration 或 DB write。"""
execute_requested = bool(catalog_review.get("execute_requested"))
catalog_state = catalog_review.get("catalog_state")
seed_state = catalog_review.get("seed_state")
seed_probe_error_tolerated = bool(
execute_requested
and catalog_state == "not_applied"
and seed_state == "probe_error"
)
read_only_probe_completed = bool(
catalog_review.get("read_only_query_executed")
and catalog_state != "probe_error"
)
safety_checks = {
"feature_flags_default_safe": bool(
not runtime_status.enabled
and not runtime_status.crawler_enabled
and not runtime_status.write_enabled
),
"database_write_blocked": bool(not runtime_status.database_write_allowed),
"scheduler_detached": bool(not runtime_status.scheduler_attached),
"catalog_review_safe": bool(
catalog_review.get("review_ready")
and not catalog_review.get("database_write_executed")
and not catalog_review.get("database_commit_executed")
and not catalog_review.get("database_session_created")
and not catalog_review.get("migration_executed")
and not catalog_review.get("rollback_executed")
),
"read_only_probe_completed_when_requested": bool(
not execute_requested or read_only_probe_completed
),
"seed_probe_error_tolerable": bool(
seed_state != "probe_error" or seed_probe_error_tolerated
),
}
live_smoke_passed = bool(
execute_requested
and read_only_probe_completed
and all(safety_checks.values())
)
smoke_result = _smoke_result(
catalog_review,
execute_requested=execute_requested,
)
return {
"mode": "migration_live_smoke_preview",
"execute_requested": execute_requested,
"smoke_result": smoke_result,
"live_smoke_passed": live_smoke_passed,
"catalog_state": catalog_state,
"seed_state": seed_state,
"risk_level": catalog_review.get("risk_level"),
"apply_path": catalog_review.get("apply_path"),
"read_only_probe_completed": read_only_probe_completed,
"seed_probe_error_tolerated": seed_probe_error_tolerated,
"ready_for_ai_controlled_migration_review": bool(
catalog_review.get("ready_for_ai_controlled_migration_review")
or catalog_review.get(_READY_FOR_MIGRATION_REVIEW_KEY)
),
_READY_FOR_MIGRATION_REVIEW_KEY: bool(
catalog_review.get("ready_for_ai_controlled_migration_review")
or catalog_review.get(_READY_FOR_MIGRATION_REVIEW_KEY)
),
"ready_to_apply_migration": False,
"migration_executed": False,
"rollback_executed": False,
"database_connection_opened": bool(
catalog_review.get("database_connection_opened")
),
"read_only_query_executed": bool(
catalog_review.get("read_only_query_executed")
),
"database_session_created": False,
"explicit_transaction_opened": False,
"database_write_executed": False,
"database_commit_executed": False,
"external_network_executed": False,
"scheduler_attached": False,
"api_executes_migration": False,
"api_executes_rollback": False,
"safety_checks": safety_checks,
"blocked_reasons": _build_blocked_reasons(
execute_requested=execute_requested,
catalog_review=catalog_review,
safety_checks=safety_checks,
),
"catalog_review_summary": {
"catalog_state": catalog_state,
"seed_state": seed_state,
"risk_level": catalog_review.get("risk_level"),
"apply_path": catalog_review.get("apply_path"),
"table_catalog": catalog_review.get("table_catalog") or {},
"seed_catalog": catalog_review.get("seed_catalog") or {},
"finding_count": len(catalog_review.get("findings") or []),
"findings": catalog_review.get("findings") or [],
},
"operator_next_steps": [
{
"key": "run_live_smoke_execute_true",
"label": "AI 受控呼叫 /api/market_intel/migration_live_smoke?execute=true 執行只讀 smoke",
"status": "completed" if execute_requested else "required",
},
{
"key": "interpret_smoke_result",
"label": "依 smoke_result 判斷正式 DB 目前是否尚未套表、已套表、partial schema 或 probe error",
"status": "required",
},
{
"key": "ai_controlled_migration_review_only",
"label": "只有 smoke 顯示 not_applied 且 controlled apply package ready 後,才可另開維護窗口受控執行 migration",
"status": "blocked_until_controlled_apply_package_ready",
},
],
"controlled_probe_targets": [
"/api/market_intel/migration_live_smoke?execute=true",
"/api/market_intel/migration_catalog_review?execute=true",
"/api/market_intel/schema_db_probe?execute=true",
"/api/market_intel/platform_seed_db_diff?execute=true",
],
_PROBE_TARGETS_COMPAT_KEY: [
"/api/market_intel/migration_live_smoke?execute=true",
"/api/market_intel/migration_catalog_review?execute=true",
"/api/market_intel/schema_db_probe?execute=true",
"/api/market_intel/platform_seed_db_diff?execute=true",
],
}