[V10.280] 補市場情報 Phase81 AI summary run package | market_intel
All checks were successful
CD Pipeline / deploy (push) Successful in 1m6s

This commit is contained in:
OoO
2026-05-19 22:12:55 +08:00
parent dacf5f305a
commit f911f96d1a
10 changed files with 1138 additions and 88 deletions

View File

@@ -0,0 +1,462 @@
"""候選審核 queue AI summary run package 預覽。
本模組只把 archive summary 與 AI summary preflight 組成手動 Ollama 摘要任務包;
不呼叫 LLM、不派送 Telegram、不讀 approval token、不執行 CLI、不寫檔、
不寫 DB、不更新 review_state、不 commit、不掛 scheduler。
"""
FORBIDDEN_TOKEN_KEYWORDS = (
"approval_token",
"approval-token",
"market_intel_queue_write_approval",
)
SAFE_TOKEN_METADATA_KEYS = {
"approval_token_present",
"approval_token_valid",
"approval_token_secret_configured",
}
SAFE_APPROVAL_ENV_VAR = "MARKET_INTEL_QUEUE_WRITE_APPROVAL"
TARGET_TABLE = "market_alert_review_queue"
def _as_dict(value):
return value if isinstance(value, dict) else {}
def _as_list(value):
if value is None:
return []
if isinstance(value, (list, tuple, set)):
return list(value)
return [value]
def _contains_forbidden_token_key(value):
if isinstance(value, dict):
for key, nested in value.items():
normalized_key = str(key).lower()
if normalized_key in SAFE_TOKEN_METADATA_KEYS and isinstance(nested, bool):
continue
if normalized_key == "approval_env_var" and nested == SAFE_APPROVAL_ENV_VAR:
continue
if any(token_key in normalized_key for token_key in FORBIDDEN_TOKEN_KEYWORDS):
return True
if _contains_forbidden_token_key(nested):
return True
elif isinstance(value, list):
return any(_contains_forbidden_token_key(item) for item in value)
return False
def _safe_text(value, limit=300):
if value is None:
return None
text = str(value).strip()
return text[:limit] if text else None
def _side_effects_clear(*payloads):
blocked_keys = (
"api_executes_cli",
"api_reads_approval_token",
"api_writes_file",
"api_writes_database",
"api_updates_review_state",
"run_package_file_written",
"summary_file_written",
"summary_record_written",
"summary_manifest_written",
"database_write_executed",
"database_commit_executed",
"review_state_update_executed",
"llm_call_executed",
"ollama_call_executed",
"gemini_call_executed",
"telegram_dispatched",
"ai_summary_generated",
"external_network_executed",
"scheduler_attached",
"writes_executed",
"would_write_database",
)
return all(
not _as_dict(payload).get(key)
for payload in payloads
for key in blocked_keys
)
def _operator_summary(operator_evidence):
operator_evidence = _as_dict(operator_evidence)
return {
"provided_keys": sorted(operator_evidence.keys()),
"operator_confirmed_ai_summary_run_package": bool(
operator_evidence.get("operator_confirmed_ai_summary_run_package")
or operator_evidence.get("operator_confirmed_ai_summary_package")
),
"operator_confirmed_ollama_first": bool(
operator_evidence.get("operator_confirmed_ollama_first")
or operator_evidence.get("operator_confirmed_ollama_cascade")
),
"operator_confirmed_gemini_backup_only": bool(
operator_evidence.get("operator_confirmed_gemini_backup_only")
or operator_evidence.get("operator_confirmed_gemini_fallback_only")
),
"operator_confirmed_manual_llm_only": bool(
operator_evidence.get("operator_confirmed_manual_llm_only")
or operator_evidence.get("operator_confirmed_manual_ollama_summary_run")
),
"operator_confirmed_no_api_llm_call": bool(
operator_evidence.get("operator_confirmed_no_api_llm_call")
or operator_evidence.get("operator_confirmed_no_llm_call")
),
"operator_confirmed_no_telegram_dispatch": bool(
operator_evidence.get("operator_confirmed_no_telegram_dispatch")
),
"operator_confirmed_no_scheduler_attach": bool(
operator_evidence.get("operator_confirmed_no_scheduler_attach")
),
"operator_confirmed_no_api_db_write": bool(
operator_evidence.get("operator_confirmed_no_api_db_write")
),
"ai_summary_run_package_artifact_path": _safe_text(
operator_evidence.get("ai_summary_run_package_artifact_path")
or operator_evidence.get("summary_run_package_artifact_path")
),
"ai_summary_output_artifact_path": _safe_text(
operator_evidence.get("ai_summary_output_artifact_path")
or operator_evidence.get("summary_output_artifact_path")
),
"approval_token_submitted_to_api": _contains_forbidden_token_key(
operator_evidence
),
}
def _archive_summary_payload(archive_summary):
summary = _as_dict(archive_summary)
artifact_paths = _as_dict(summary.get("artifact_paths"))
return {
"provided": bool(summary),
"mode": summary.get("mode"),
"archive_summary_ready": bool(summary.get("archive_summary_ready")),
"summary_input_ready": bool(summary.get("summary_input_ready")),
"ready_for_ai_summary_review": bool(summary.get("ready_for_ai_summary_review")),
"expected_dedupe_keys": sorted(
str(key) for key in _as_list(summary.get("expected_dedupe_keys"))
),
"found_dedupe_keys": sorted(
str(key) for key in _as_list(summary.get("found_dedupe_keys"))
),
"missing_dedupe_keys": sorted(
str(key) for key in _as_list(summary.get("missing_dedupe_keys"))
),
"state_mismatches": _as_list(summary.get("state_mismatches")),
"row_summary_count": int(summary.get("row_summary_count") or 0),
"summary_sections": _as_list(summary.get("summary_sections")),
"summary_section_keys": [
_as_dict(section).get("key")
for section in _as_list(summary.get("summary_sections"))
if _as_dict(section).get("key")
],
"artifact_paths": artifact_paths,
"read_only_query_executed": bool(summary.get("read_only_query_executed")),
"database_connection_opened": bool(summary.get("database_connection_opened")),
"database_write_executed": bool(summary.get("database_write_executed")),
"database_commit_executed": bool(summary.get("database_commit_executed")),
"review_state_update_executed": bool(summary.get("review_state_update_executed")),
"scheduler_attached": bool(summary.get("scheduler_attached")),
"llm_call_executed": bool(summary.get("llm_call_executed")),
"telegram_dispatched": bool(summary.get("telegram_dispatched")),
"ai_summary_generated": bool(summary.get("ai_summary_generated")),
}
def _preflight_payload(ai_summary_preflight):
preflight = _as_dict(ai_summary_preflight)
route_policy = _as_dict(preflight.get("model_route_policy"))
return {
"provided": bool(preflight),
"mode": preflight.get("mode"),
"ai_summary_preflight_ready": bool(preflight.get("ai_summary_preflight_ready")),
"ready_for_manual_ollama_summary_run": bool(
preflight.get("ready_for_manual_ollama_summary_run")
),
"model_route_policy": route_policy,
"safe_boundaries": _as_list(preflight.get("safe_boundaries")),
"blocked_reasons": _as_list(preflight.get("blocked_reasons")),
"llm_call_executed": bool(preflight.get("llm_call_executed")),
"ollama_call_executed": bool(preflight.get("ollama_call_executed")),
"gemini_call_executed": bool(preflight.get("gemini_call_executed")),
"telegram_dispatched": bool(preflight.get("telegram_dispatched")),
"database_write_executed": bool(preflight.get("database_write_executed")),
"scheduler_attached": bool(preflight.get("scheduler_attached")),
}
def _summary_output_schema():
return {
"schema_version": "market_intel_ai_summary_v1",
"required_fields": [
"headline",
"executive_summary",
"key_findings",
"risk_flags",
"recommended_actions",
"evidence_refs",
"model_route",
],
"field_contract": {
"headline": "短標題,必須基於 archive summary facts",
"executive_summary": "3 到 5 句繁體中文摘要",
"key_findings": "只列資料中可驗證的發現",
"risk_flags": "只列有證據的風險,不推測未觀測資料",
"recommended_actions": "每一項都要附 evidence_refs",
"evidence_refs": "引用 dedupe keys、summary sections 或 artifact paths",
"model_route": "記錄 Ollama host 或 Gemini fallback reason",
},
}
def _prompt_contract(*, summary, preflight, operator):
route_policy = preflight["model_route_policy"]
return {
"contract_version": "market_intel_ai_summary_prompt_v1",
"language": "zh-TW",
"primary_policy": route_policy.get("primary_policy"),
"fallback_policy": route_policy.get("fallback_policy"),
"ollama_cascade": _as_list(route_policy.get("primary_cascade")),
"forbidden_ollama_node": route_policy.get("app_host_forbidden_as_ollama_node"),
"input_contract": {
"summary_section_keys": summary["summary_section_keys"],
"expected_dedupe_keys": summary["expected_dedupe_keys"],
"artifact_paths": summary["artifact_paths"],
},
"operator_artifacts": {
"run_package_artifact_path": operator[
"ai_summary_run_package_artifact_path"
],
"summary_output_artifact_path": operator[
"ai_summary_output_artifact_path"
],
},
"prompt_sections": [
{
"key": "system_boundary",
"rule": "Only summarize facts present in archive summary input.",
},
{
"key": "evidence_grounding",
"rule": "Every recommendation must reference a dedupe key or artifact path.",
},
{
"key": "no_action_execution",
"rule": "Do not claim Telegram, DB write, scheduler, or price action was executed.",
},
],
"output_schema": _summary_output_schema(),
}
def _run_package_gates(*, summary, preflight, operator, prompt_contract):
route_policy = preflight["model_route_policy"]
output_schema = prompt_contract["output_schema"]
return [
{
"key": "archive_summary_ready",
"label": "archive summary input 必須已通過",
"passed": bool(
summary["provided"]
and summary["mode"] == "candidate_queue_review_archive_summary_preview"
and summary["archive_summary_ready"]
and summary["summary_input_ready"]
and summary["ready_for_ai_summary_review"]
),
},
{
"key": "ai_summary_preflight_ready",
"label": "AI summary preflight 必須已通過",
"passed": bool(
preflight["provided"]
and preflight["mode"]
== "candidate_queue_review_ai_summary_preflight_preview"
and preflight["ai_summary_preflight_ready"]
and preflight["ready_for_manual_ollama_summary_run"]
),
},
{
"key": "ollama_first_route_locked",
"label": "run package 必須鎖定 Ollama-first 與 Gemini 備援政策",
"passed": bool(
route_policy.get("primary_policy") == "ollama_first"
and len(_as_list(route_policy.get("primary_cascade"))) == 3
and route_policy.get("fallback_policy")
== "gemini_backup_only_after_ollama_cascade_failure"
and route_policy.get("app_host_forbidden_as_ollama_node")
== "192.168.0.188"
),
},
{
"key": "prompt_contract_complete",
"label": "prompt contract 必須包含輸入、路由與禁止事項",
"passed": bool(
prompt_contract["input_contract"]["summary_section_keys"]
and prompt_contract["input_contract"]["expected_dedupe_keys"]
and prompt_contract["prompt_sections"]
and prompt_contract["operator_artifacts"][
"run_package_artifact_path"
]
),
},
{
"key": "summary_output_schema_complete",
"label": "摘要輸出 schema 必須包含必要欄位",
"passed": bool(
output_schema["schema_version"]
and len(output_schema["required_fields"]) >= 7
and "evidence_refs" in output_schema["required_fields"]
),
},
{
"key": "operator_confirmed_ai_summary_run_package",
"label": "操作員確認本步只產生手動 Ollama 摘要任務包",
"passed": bool(
operator["operator_confirmed_ai_summary_run_package"]
and operator["operator_confirmed_ollama_first"]
and operator["operator_confirmed_gemini_backup_only"]
and operator["operator_confirmed_manual_llm_only"]
and operator["operator_confirmed_no_api_llm_call"]
and operator["operator_confirmed_no_telegram_dispatch"]
and operator["operator_confirmed_no_api_db_write"]
and operator["operator_confirmed_no_scheduler_attach"]
),
},
{
"key": "ai_summary_run_package_artifacts_recorded",
"label": "run package 與 summary output artifact path 必須由操作員提供",
"passed": bool(
operator["ai_summary_run_package_artifact_path"]
and operator["ai_summary_output_artifact_path"]
),
},
{
"key": "ai_summary_run_package_no_approval_token_submitted_to_api",
"label": "payload 不得包含一次性 approval token key",
"passed": not operator["approval_token_submitted_to_api"],
},
{
"key": "ai_summary_run_package_has_no_side_effects",
"label": "run package 不得呼叫 LLM、派送 Telegram、寫檔、寫 DB 或掛 scheduler",
"passed": _side_effects_clear(summary, preflight),
},
]
def build_candidate_queue_review_ai_summary_run_package(
*,
archive_summary,
ai_summary_preflight,
operator_evidence=None,
execute_requested=False,
):
"""建立手動 Ollama AI summary run package 預覽;不呼叫模型或執行副作用。"""
summary = _archive_summary_payload(archive_summary)
preflight = _preflight_payload(ai_summary_preflight)
operator = _operator_summary(operator_evidence)
prompt_contract = _prompt_contract(
summary=summary,
preflight=preflight,
operator=operator,
)
gates = _run_package_gates(
summary=summary,
preflight=preflight,
operator=operator,
prompt_contract=prompt_contract,
)
blocked_reasons = [gate["key"] for gate in gates if not gate["passed"]]
run_package_ready = bool(not blocked_reasons)
return {
"mode": "candidate_queue_review_ai_summary_run_package_preview",
"target_table": TARGET_TABLE,
"target_operation": "package_ollama_first_ai_summary_manual_run",
"execute_requested": bool(execute_requested),
"ai_summary_run_package_ready": run_package_ready,
"ready_for_manual_ollama_summary_run": run_package_ready,
"ready_for_next_manual_phase": run_package_ready,
"ready_for_ai_summary_generation": False,
"ready_for_llm_call": False,
"ready_for_telegram_dispatch": False,
"ready_for_api_review_state_update": False,
"ready_for_api_database_write": False,
"ready_for_scheduler_attach": False,
"api_executes_cli": False,
"api_executes_llm": False,
"api_reads_approval_token": False,
"api_writes_file": False,
"api_writes_database": False,
"api_updates_review_state": False,
"approval_record_written": False,
"decision_record_written": False,
"run_package_file_written": False,
"summary_file_written": False,
"summary_record_written": False,
"summary_manifest_written": False,
"ai_summary_generated": False,
"llm_call_executed": False,
"ollama_call_executed": False,
"gemini_call_executed": False,
"telegram_dispatched": False,
"review_state_update_executed": False,
"read_only_query_executed": summary["read_only_query_executed"],
"database_connection_opened": summary["database_connection_opened"],
"database_session_created": False,
"explicit_transaction_opened": False,
"transaction_opened": False,
"transaction_committed": False,
"database_write_executed": False,
"database_commit_executed": False,
"database_rollback_executed": False,
"external_network_executed": False,
"scheduler_attached": False,
"writes_executed": False,
"would_write_database": False,
"expected_dedupe_keys": summary["expected_dedupe_keys"],
"found_dedupe_keys": summary["found_dedupe_keys"],
"missing_dedupe_keys": summary["missing_dedupe_keys"],
"state_mismatches": summary["state_mismatches"],
"row_summary_count": summary["row_summary_count"],
"summary_section_keys": summary["summary_section_keys"],
"model_route_policy": preflight["model_route_policy"],
"prompt_contract": prompt_contract,
"summary_output_schema": prompt_contract["output_schema"],
"operator_ai_summary_run_package": operator,
"blocked_reasons": blocked_reasons,
"gates": gates,
"next_operator_steps": [
"確認 run package 與 summary output artifact path",
"手動摘要執行必須使用 Ollama-first 三主機級聯",
"Gemini 只能在 Ollama cascade 全失敗後備援,並記錄 fallback reason",
"摘要結果回填 DB 或 Telegram 必須另開後續 gate本步不執行",
],
"safe_boundaries": [
"do_not_call_llm_from_ai_summary_run_package",
"do_not_dispatch_telegram_from_ai_summary_run_package",
"do_not_write_run_package_file_from_api",
"do_not_write_summary_file_from_api",
"do_not_insert_summary_record_from_api",
"do_not_update_review_state_from_ai_summary_run_package",
"do_not_read_approval_token_from_ai_summary_run_package",
"do_not_execute_cli_from_ai_summary_run_package",
"do_not_attach_scheduler_from_ai_summary_run_package",
"manual_ollama_summary_run_only",
"ollama_cascade_must_use_gcp_a_then_gcp_b_then_111",
"gemini_backup_only_after_ollama_cascade_failure",
"host_188_forbidden_as_ollama_node",
"ai_summary_run_package_preview_only",
"no_remove_orphans",
"no_momo_db_lifecycle_change",
],
}

View File

@@ -28,6 +28,7 @@ from services.market_intel.candidate_queue_review_decision_post_closeout_invento
from services.market_intel.candidate_queue_review_completion_archive import build_candidate_queue_review_completion_archive
from services.market_intel.candidate_queue_review_archive_summary import build_candidate_queue_review_archive_summary
from services.market_intel.candidate_queue_review_ai_summary_preflight import build_candidate_queue_review_ai_summary_preflight
from services.market_intel.candidate_queue_review_ai_summary_run_package import build_candidate_queue_review_ai_summary_run_package
BLOCKED_RUN_REVIEW_KEYS = (
@@ -37,12 +38,14 @@ BLOCKED_RUN_REVIEW_KEYS = (
"ready_for_llm_call",
"ready_for_telegram_dispatch",
"api_executes_cli",
"api_executes_llm",
"api_reads_approval_token",
"api_writes_file",
"api_writes_database",
"api_updates_review_state",
"approval_record_written",
"decision_record_written",
"run_package_file_written",
"summary_file_written",
"summary_record_written",
"summary_manifest_written",
@@ -64,7 +67,7 @@ BLOCKED_RUN_REVIEW_KEYS = (
"writes_executed",
"would_write_database",
)
PRODUCTION_SMOKE_TARGETS = ("/health", "/market_intel", "/api/market_intel/status", "/api/market_intel/deployment_readiness", "/api/market_intel/schema_smoke", "/api/market_intel/schema_db_probe", "/api/market_intel/platform_seed_db_diff", "/api/market_intel/legacy_source_bridge", "/api/market_intel/mcp_readiness", "/api/market_intel/mcp_tool_contract", "/api/market_intel/mcp_deploy_preflight", "/api/market_intel/mcp_activation_runbook", "/api/market_intel/mcp_fetch_gate", "/api/market_intel/scheduler_plan", "/api/market_intel/manual_sample_plan", "/api/market_intel/manual_sample_acceptance", "/api/market_intel/manual_sample_review", "/api/market_intel/match_review_plan", "/api/market_intel/opportunity_plan", "/api/market_intel/opportunity_scoring_plan", "/api/market_intel/opportunity_evidence_plan", "/api/market_intel/opportunity_alert_plan", "/api/market_intel/migration_apply_drill", "/api/market_intel/migration_catalog_review", "/api/market_intel/migration_live_smoke", "/api/market_intel/live_db_inventory", "/api/market_intel/manual_sample_review/candidate_queue_writer_postwrite_smoke", "/api/market_intel/manual_sample_review/candidate_queue_writer_operator_drill", "/api/market_intel/manual_sample_review/candidate_queue_writer_run_package", "/api/market_intel/manual_sample_review/candidate_queue_writer_run_readiness", "/api/market_intel/manual_sample_review/candidate_queue_writer_run_receipt", "/api/market_intel/manual_sample_review/candidate_queue_writer_run_closeout", "/api/market_intel/manual_sample_review/candidate_queue_review_handoff", "/api/market_intel/manual_sample_review/candidate_queue_review_inventory", "/api/market_intel/manual_sample_review/candidate_queue_review_decision", "/api/market_intel/manual_sample_review/candidate_queue_review_decision_approval", "/api/market_intel/manual_sample_review/candidate_queue_review_decision_transaction", "/api/market_intel/manual_sample_review/candidate_queue_review_decision_writer_preflight", "/api/market_intel/manual_sample_review/candidate_queue_review_decision_writer_postwrite_smoke", "/api/market_intel/manual_sample_review/candidate_queue_review_decision_writer_operator_drill", "/api/market_intel/manual_sample_review/candidate_queue_review_decision_writer_run_package", "/api/market_intel/manual_sample_review/candidate_queue_review_decision_writer_run_readiness", "/api/market_intel/manual_sample_review/candidate_queue_review_decision_writer_run_receipt", "/api/market_intel/manual_sample_review/candidate_queue_review_decision_writer_run_closeout", "/api/market_intel/manual_sample_review/candidate_queue_review_decision_post_closeout_inventory", "/api/market_intel/manual_sample_review/candidate_queue_review_completion_archive", "/api/market_intel/manual_sample_review/candidate_queue_review_archive_summary", "/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_preflight", "/api/market_intel/manual_sample_review/candidate_queue_review_decision_writer_status")
PRODUCTION_SMOKE_TARGETS = ("/health", "/market_intel", "/api/market_intel/status", "/api/market_intel/deployment_readiness", "/api/market_intel/schema_smoke", "/api/market_intel/schema_db_probe", "/api/market_intel/platform_seed_db_diff", "/api/market_intel/legacy_source_bridge", "/api/market_intel/mcp_readiness", "/api/market_intel/mcp_tool_contract", "/api/market_intel/mcp_deploy_preflight", "/api/market_intel/mcp_activation_runbook", "/api/market_intel/mcp_fetch_gate", "/api/market_intel/scheduler_plan", "/api/market_intel/manual_sample_plan", "/api/market_intel/manual_sample_acceptance", "/api/market_intel/manual_sample_review", "/api/market_intel/match_review_plan", "/api/market_intel/opportunity_plan", "/api/market_intel/opportunity_scoring_plan", "/api/market_intel/opportunity_evidence_plan", "/api/market_intel/opportunity_alert_plan", "/api/market_intel/migration_apply_drill", "/api/market_intel/migration_catalog_review", "/api/market_intel/migration_live_smoke", "/api/market_intel/live_db_inventory", "/api/market_intel/manual_sample_review/candidate_queue_writer_postwrite_smoke", "/api/market_intel/manual_sample_review/candidate_queue_writer_operator_drill", "/api/market_intel/manual_sample_review/candidate_queue_writer_run_package", "/api/market_intel/manual_sample_review/candidate_queue_writer_run_readiness", "/api/market_intel/manual_sample_review/candidate_queue_writer_run_receipt", "/api/market_intel/manual_sample_review/candidate_queue_writer_run_closeout", "/api/market_intel/manual_sample_review/candidate_queue_review_handoff", "/api/market_intel/manual_sample_review/candidate_queue_review_inventory", "/api/market_intel/manual_sample_review/candidate_queue_review_decision", "/api/market_intel/manual_sample_review/candidate_queue_review_decision_approval", "/api/market_intel/manual_sample_review/candidate_queue_review_decision_transaction", "/api/market_intel/manual_sample_review/candidate_queue_review_decision_writer_preflight", "/api/market_intel/manual_sample_review/candidate_queue_review_decision_writer_postwrite_smoke", "/api/market_intel/manual_sample_review/candidate_queue_review_decision_writer_operator_drill", "/api/market_intel/manual_sample_review/candidate_queue_review_decision_writer_run_package", "/api/market_intel/manual_sample_review/candidate_queue_review_decision_writer_run_readiness", "/api/market_intel/manual_sample_review/candidate_queue_review_decision_writer_run_receipt", "/api/market_intel/manual_sample_review/candidate_queue_review_decision_writer_run_closeout", "/api/market_intel/manual_sample_review/candidate_queue_review_decision_post_closeout_inventory", "/api/market_intel/manual_sample_review/candidate_queue_review_completion_archive", "/api/market_intel/manual_sample_review/candidate_queue_review_archive_summary", "/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_preflight", "/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_run_package", "/api/market_intel/manual_sample_review/candidate_queue_review_decision_writer_status")
def _run_review_preview_safe(payload, mode):
@@ -215,6 +218,10 @@ def build_deployment_readiness_preview(
candidate_queue_review_ai_summary_preflight = build_candidate_queue_review_ai_summary_preflight(
archive_summary=candidate_queue_review_archive_summary,
)
candidate_queue_review_ai_summary_run_package = build_candidate_queue_review_ai_summary_run_package(
archive_summary=candidate_queue_review_archive_summary,
ai_summary_preflight=candidate_queue_review_ai_summary_preflight,
)
checks = {
"schema_smoke_passed": bool(schema_smoke["passed"]),
"feature_flags_default_safe": bool(
@@ -497,6 +504,10 @@ def build_deployment_readiness_preview(
candidate_queue_review_ai_summary_preflight,
"candidate_queue_review_ai_summary_preflight_preview",
),
"candidate_queue_review_ai_summary_run_package_preview_safe": _run_review_preview_safe(
candidate_queue_review_ai_summary_run_package,
"candidate_queue_review_ai_summary_run_package_preview",
),
"candidate_queue_review_decision_writer_cli_status_safe": _run_review_preview_safe(
candidate_queue_review_decision_writer_status,
"candidate_queue_review_decision_writer_cli_blocked",
@@ -744,6 +755,7 @@ def build_deployment_readiness_preview(
"candidate_queue_review_completion_archive": candidate_queue_review_completion_archive,
"candidate_queue_review_archive_summary": candidate_queue_review_archive_summary,
"candidate_queue_review_ai_summary_preflight": candidate_queue_review_ai_summary_preflight,
"candidate_queue_review_ai_summary_run_package": candidate_queue_review_ai_summary_run_package,
"candidate_queue_review_decision_writer_status": candidate_queue_review_decision_writer_status,
"match_review_plan": match_review_plan,
"opportunity_plan": opportunity_plan,

View File

@@ -1,3 +1,3 @@
"""市場情報 rollout phase 單一來源。"""
MARKET_INTEL_PHASE = "phase_80_candidate_queue_review_ai_summary_preflight"
MARKET_INTEL_PHASE = "phase_81_candidate_queue_review_ai_summary_run_package"