135 lines
5.0 KiB
Python
135 lines
5.0 KiB
Python
"""市場情報正式寫入前的人工批准 runbook。
|
||
|
||
本模組只產生 gate 與操作順序,不建立 DB session、不執行 migration、不寫入資料。
|
||
"""
|
||
|
||
|
||
def _status_value(status, key, default=False):
|
||
return bool(getattr(status, key, default))
|
||
|
||
|
||
def build_write_approval_runbook(
|
||
*,
|
||
phase,
|
||
status,
|
||
schema_smoke,
|
||
seed_plan,
|
||
write_guard,
|
||
writer_plan,
|
||
):
|
||
"""建立正式 seed write 前的 read-only runbook。"""
|
||
gates = [
|
||
{
|
||
"key": "schema_smoke_passed",
|
||
"label": "ORM metadata smoke 已通過,八張 market_* table 與 market_platforms 欄位完整",
|
||
"passed": bool(schema_smoke.get("passed")),
|
||
},
|
||
{
|
||
"key": "backup_completed",
|
||
"label": "已在正式推版前執行 python backup_system.py",
|
||
"passed": False,
|
||
},
|
||
{
|
||
"key": "migration_file_reviewed",
|
||
"label": "market_* schema migration 已人工審核,且不 drop/alter 既有業績資料表",
|
||
"passed": False,
|
||
},
|
||
{
|
||
"key": "feature_flags_enabled_for_write_window",
|
||
"label": "寫入窗口才可同時啟用 MARKET_INTEL_ENABLED 與 MARKET_INTEL_WRITE_ENABLED",
|
||
"passed": bool(_status_value(status, "enabled") and _status_value(status, "write_enabled")),
|
||
},
|
||
{
|
||
"key": "database_write_allowed",
|
||
"label": "runtime database_write_allowed 為 true",
|
||
"passed": bool(_status_value(status, "database_write_allowed")),
|
||
},
|
||
{
|
||
"key": "manual_operator_approval",
|
||
"label": "操作者已明確批准一次性 market_platforms seed upsert",
|
||
"passed": False,
|
||
},
|
||
{
|
||
"key": "rollback_plan_reviewed",
|
||
"label": "已確認回復策略:關閉 flags、app-only rollback、必要時清理 seed rows",
|
||
"passed": False,
|
||
},
|
||
{
|
||
"key": "production_smoke_targets_defined",
|
||
"label": "已定義 /health 與市場情報 API smoke targets",
|
||
"passed": True,
|
||
},
|
||
]
|
||
blocked_reasons = [gate["key"] for gate in gates if not gate["passed"]]
|
||
|
||
return {
|
||
"phase": phase,
|
||
"mode": "approval_runbook_read_only",
|
||
"ready_for_real_write": False,
|
||
"writes_executed": False,
|
||
"would_write_database": False,
|
||
"database_session_created": False,
|
||
"database_commit_executed": False,
|
||
"external_network_executed": False,
|
||
"scheduler_attached": False,
|
||
"approval_required": True,
|
||
"approval_token_present": False,
|
||
"blocked_reasons": blocked_reasons,
|
||
"approval_gates": gates,
|
||
"seed_count": int(seed_plan.get("seed_count") or 0),
|
||
"writer_operation_count": int(writer_plan.get("operation_count") or 0),
|
||
"schema_smoke": schema_smoke,
|
||
"write_guard_summary": {
|
||
"ready_to_write": bool(write_guard.get("ready_to_write")),
|
||
"would_write_database": bool(write_guard.get("would_write_database")),
|
||
"blocked_reasons": write_guard.get("blocked_reasons", []),
|
||
},
|
||
"operator_sequence": [
|
||
{
|
||
"key": "scope_review",
|
||
"label": "確認 git diff 只包含 market_intel、ADR/TODO、版本與測試",
|
||
},
|
||
{
|
||
"key": "backup",
|
||
"label": "執行 python backup_system.py,保存正式環境回復點",
|
||
},
|
||
{
|
||
"key": "migration_apply_window",
|
||
"label": "在維護窗口套用 market_* migration,不觸碰 momo-db 容器生命週期",
|
||
},
|
||
{
|
||
"key": "seed_preview_compare",
|
||
"label": "比對 platform_seed_writer_plan 的 4 筆 upsert preview",
|
||
},
|
||
{
|
||
"key": "one_time_seed_write",
|
||
"label": "另開明確批准後才允許一次性 seed writer 真寫入",
|
||
},
|
||
{
|
||
"key": "post_write_smoke",
|
||
"label": "驗證 /health、/market_intel、schema_smoke 與 deployment_readiness",
|
||
},
|
||
],
|
||
"rollback_plan": [
|
||
{
|
||
"key": "disable_flags",
|
||
"label": "立刻關閉 MARKET_INTEL_ENABLED、MARKET_INTEL_CRAWLER_ENABLED、MARKET_INTEL_WRITE_ENABLED",
|
||
},
|
||
{
|
||
"key": "app_only_rollback",
|
||
"label": "回退 momo-app 程式版本,只 recreate momo-app,不碰 momo-db",
|
||
},
|
||
{
|
||
"key": "seed_rows_cleanup",
|
||
"label": "若唯一異常來自 platform seed,可在人工審核後刪除或停用 market_platforms seed rows",
|
||
},
|
||
],
|
||
"hard_safety_boundaries": [
|
||
"no_external_crawling_during_seed_write",
|
||
"no_scheduler_attach_during_seed_write",
|
||
"no_momo_db_container_lifecycle_change",
|
||
"no_remove_orphans",
|
||
"health_probe_uses_health_endpoint_only",
|
||
],
|
||
}
|