diff --git a/docs/AI_INTELLIGENCE_MODULE_SOT.md b/docs/AI_INTELLIGENCE_MODULE_SOT.md index 18e4ea5..dcbd742 100644 --- a/docs/AI_INTELLIGENCE_MODULE_SOT.md +++ b/docs/AI_INTELLIGENCE_MODULE_SOT.md @@ -88,6 +88,7 @@ - 2026-07-02 起 PChome controlled-apply drift 必須提供 read-only rollback / re-apply recommendation package;`/api/ai/pchome-growth/mapping-backlog/direct-mapping-retry-candidate-exception-controlled-apply-drift-recovery-package` 會輸出 drift recovery actions、controlled re-apply SQL shape、rollback SQL shape、selector bindings、acceptance gates 與 artifact hash verifier。此 package 不執行 SQL、不寫 DB,0 drift 時必須產生 no-op evidence,drift detected 時才輸出 ready_for_controlled_reapply actions。 - 2026-07-02 起 PChome controlled-apply 必須提供 compact latest readback endpoint;`/api/ai/pchome-growth/mapping-backlog/direct-mapping-retry-candidate-exception-controlled-apply-compact-readback-package` 會收斂 apply、receipt replay、drift verifier、drift recovery 四段 receipt,輸出 product status、next machine action、selector readback、drift count、recovery action count 與 artifact hash 狀態。此 endpoint 是後續產品 UI 的主要資料來源,不執行 SQL、不寫 DB。 - 2026-07-02 起 PChome controlled-apply artifacts 必須提供 read-only retention policy;`/api/ai/pchome-growth/mapping-backlog/direct-mapping-retry-candidate-exception-controlled-apply-artifact-retention-package` 會掃描 verifier inputs、identity readback、controlled apply preflight、executor、replay、drift verifier、drift recovery、compact readback 八類 artifacts,依 `keep_latest_per_family` 保留最新 evidence 並保護 active compact readback chain,只輸出 prune candidates 與 retention receipt,不直接刪檔、不寫 DB、不執行 destructive prune。 +- 2026-07-02 起 PChome dashboard 第一視窗必須呈現 AI automation product truth:`_load_pchome_growth_command_center` 需 read-only 聚合 receipt replay、drift verifier、drift recovery、compact readback 與 artifact retention policy,產品面直接顯示 selector readback、drift count、retention 保留數與 automation lane 狀態;不得退回只有 raw API / artifact / log 才看得到 AI 自動化結果的模式。 - V10.644 起 `/ai_intelligence` 的商品明細列不得只用句子描述比價;每列必須顯示 PChome 價格、MOMO 參考價、差距、可信度四格價格證據,並保留下一步按鈕。單位價候選需顯示單位價與單位,候選待確認或缺資料則以「待補 / 候選待確認」呈現,不得捏造價格。 - V10.645 起 `/ai_intelligence` 的商品明細分流切換後,必須顯示「這類商品怎麼處理」的行動摘要,包含件數、近 7 天業績、平均可信度、最大價差、代表商品與主按鈕;使用者不得只能看到商品列表而不知道下一步。 - V10.646 起 `/ai_intelligence` 的商品明細必須提供搜尋與排序;搜尋至少涵蓋商品、分類、商品編號與 MOMO 候選資訊,排序至少支援優先級、近 7 天業績、價差、下滑幅度與可信度。搜尋/排序後的行動摘要與明細列表必須使用同一批結果。 diff --git a/docs/guides/pchome_ai_automation_priority_backlog.md b/docs/guides/pchome_ai_automation_priority_backlog.md index ce7081c..b25e5bc 100644 --- a/docs/guides/pchome_ai_automation_priority_backlog.md +++ b/docs/guides/pchome_ai_automation_priority_backlog.md @@ -78,6 +78,10 @@ - `direct-mapping-retry-candidate-exception-controlled-apply-artifact-retention-package` 會掃描 controlled apply 相關 artifact families - 每個 family 依 `keep_latest_per_family` 保留最新 evidence,並額外保護 active compact readback chain - 只產生 prune candidates 與 retention receipt,不直接刪檔、不寫 DB、不執行 destructive prune +- Product dashboard first-viewport surface 已完成: + - PChome 業績成長作戰台第一視窗已接入 AI automation readiness、receipt replay、drift verifier、drift recovery、compact readback 與 artifact retention policy + - 第一視窗直接顯示「AI 全自動閉環已回讀」、selector readback、drift count、retention 保留數與 automation lane 狀態 + - 頁面讀取採 read-only package,不寫 DB、不刪 artifacts、不執行 prune - AI debt scanner 顯示產品面清空: - `PRODUCT_SURFACE_CLEAR` - `finding_count=0` @@ -85,7 +89,7 @@ 進行中 / 下一步,必須照順序: -1. 把 PChome AI automation lanes 與 compact readback 接進 product dashboard 第一視窗。 +1. 為 dashboard AI automation surface 補 UI wording guard,避免 raw engineering terms 回到產品第一視窗。 完成標準: @@ -106,18 +110,19 @@ - AI readiness 已聚合主要 automation lanes。 - Controlled apply、receipt replay、drift verifier 已進 readiness。 - 產品層 automation result 已輸出 zero primary human gates。 +- PChome dashboard 第一視窗已接入 compact readback 與 artifact retention policy。 +- 第一視窗 automation pipeline 已從「等待 verifier」改成顯示受控落地、Compact 回讀、Artifact retention 的 read-only production 狀態。 -未開始 / 下一步,必須照順序: +進行中 / 下一步,必須照順序: -1. 把 PChome AI automation lanes 放進實際 product dashboard 第一視窗。 -2. 用繁體中文營運語言呈現 lane label、狀態與下一個機器動作。 -3. 正常產品畫面隱藏 raw endpoint、commit ID、DB table name、JSON payload。 -4. 增加專業的「今日 AI 自動化狀態」摘要: +1. 用繁體中文營運語言強化 lane label、狀態與下一個機器動作。 +2. 正常產品畫面隱藏 raw endpoint、commit ID、DB table name、JSON payload。 +3. 增加專業的「今日 AI 自動化狀態」摘要: - 已自動落地什麼 - 已驗證什麼 - drift 狀態 - 下一個機器動作 -5. 增加 UI tests 或 route readback,避免產品頁退回 raw engineering wording。 +4. 增加 UI tests 或 route readback,避免產品頁退回 raw engineering wording。 完成標準: @@ -201,8 +206,8 @@ | P0.8 | Drift rollback / re-apply package | 已完成 | drift recovery package route + focused tests | 接入 compact readback | | P0.9 | Compact latest apply / replay / drift / recovery readback endpoint | 已完成 | compact readback route + focused tests | 接入 product dashboard first viewport | | P0.10 | Controlled-apply artifact retention policy | 已完成 | retention policy route + focused tests | 接入 product dashboard first viewport | -| P1.1 | Dashboard AI automation first-viewport surface | 未開始 | API readiness + compact readback + retention policy exist | 下一個實作 | -| P1.2 | UI wording guard for no raw engineering terms | 未開始 | existing guardrails only | 為新 automation surface 補 tests | +| P1.1 | Dashboard AI automation first-viewport surface | 已完成 | dashboard command center reads compact + retention packages | P1.2 wording guard | +| P1.2 | UI wording guard for no raw engineering terms | 未開始 | first viewport surface exists | 下一個實作 | | P2.1 | External benchmark encoded into requirements | 未開始 | benchmark guide exists | 更新 guardrails / tests | | P3.1 | Extend receipt / replay / drift pattern to more lanes | 未開始 | current retry lane complete | P1 後選下一條 safe lane | diff --git a/routes/dashboard_routes.py b/routes/dashboard_routes.py index 614ee87..ce2efa4 100644 --- a/routes/dashboard_routes.py +++ b/routes/dashboard_routes.py @@ -1365,6 +1365,40 @@ def _load_pchome_growth_command_center(session): 'exception_count': 0, }, }, + 'controlled_apply_compact_readback': { + 'result': 'WAITING_FOR_RETRY_EXCEPTION_CONTROLLED_APPLY_COMPACT_READBACK_BASELINE', + 'summary': { + 'target_selector_count': 0, + 'post_apply_readback_pass_count': 0, + 'drift_count': 0, + 'compact_readback_artifact_hash_match_count': 0, + 'writes_database_count': 0, + }, + 'compact_readback': { + 'status': 'waiting', + 'next_machine_action': 'run_receipt_replay_and_drift_verifier', + }, + }, + 'artifact_retention_policy': { + 'result': 'WAITING_FOR_RETRY_EXCEPTION_CONTROLLED_APPLY_ARTIFACT_RETENTION_POLICY_ARTIFACTS', + 'summary': { + 'retention_family_count': 0, + 'artifact_count': 0, + 'retained_artifact_count': 0, + 'prune_candidate_count': 0, + 'retention_prune_executes_count': 0, + 'writes_database_count': 0, + }, + 'artifact_retention': { + 'status': 'waiting', + 'keep_latest_per_family': 3, + }, + }, + 'ai_automation_first_viewport': { + 'tone': 'neutral', + 'title': 'AI 全自動閉環', + 'metric': '主流程阻斷 0 · 回讀待建立 · retention 待建立', + }, 'automation_pipeline': [], 'opportunity_sales_7d': 0, 'action_code_counts': {}, @@ -1380,6 +1414,11 @@ def _load_pchome_growth_command_center(session): from services.pchome_mapping_backlog_service import ( build_pchome_auto_policy_receipt_gate, build_pchome_direct_mapping_candidate_decision_package, + build_pchome_direct_mapping_retry_candidate_exception_controlled_apply_artifact_retention_package, + build_pchome_direct_mapping_retry_candidate_exception_controlled_apply_compact_readback_package, + build_pchome_direct_mapping_retry_candidate_exception_controlled_apply_drift_recovery_package, + build_pchome_direct_mapping_retry_candidate_exception_controlled_apply_drift_verifier_package, + build_pchome_direct_mapping_retry_candidate_exception_controlled_apply_receipt_replay_package, build_pchome_growth_ai_automation_readiness, summarize_pchome_mapping_backlog, ) @@ -1395,16 +1434,60 @@ def _load_pchome_growth_command_center(session): batch_size=8, execute_search=False, ) + controlled_apply_receipt_replay = ( + build_pchome_direct_mapping_retry_candidate_exception_controlled_apply_receipt_replay_package( + materialize_artifacts=False, + engine=engine, + ) + ) + controlled_apply_drift_verifier = ( + build_pchome_direct_mapping_retry_candidate_exception_controlled_apply_drift_verifier_package( + engine=engine, + source_receipt_replay=controlled_apply_receipt_replay, + materialize_artifacts=False, + ) + ) + controlled_apply_drift_recovery = ( + build_pchome_direct_mapping_retry_candidate_exception_controlled_apply_drift_recovery_package( + engine=engine, + source_receipt_replay=controlled_apply_receipt_replay, + source_drift_verifier=controlled_apply_drift_verifier, + materialize_artifacts=False, + ) + ) + controlled_apply_compact_readback = ( + build_pchome_direct_mapping_retry_candidate_exception_controlled_apply_compact_readback_package( + engine=engine, + source_receipt_replay=controlled_apply_receipt_replay, + source_drift_verifier=controlled_apply_drift_verifier, + source_drift_recovery=controlled_apply_drift_recovery, + materialize_artifacts=False, + ) + ) + artifact_retention_policy = ( + build_pchome_direct_mapping_retry_candidate_exception_controlled_apply_artifact_retention_package( + engine=engine, + source_compact_readback=controlled_apply_compact_readback, + keep_latest_per_family=3, + materialize_artifacts=False, + ) + ) ai_automation_readiness = build_pchome_growth_ai_automation_readiness( payload, batch_size=8, execute_search=False, execute_fetch=False, + controlled_apply_receipt_replay=controlled_apply_receipt_replay, + controlled_apply_drift_verifier=controlled_apply_drift_verifier, ) mapping_backlog_stats = mapping_summary.get('backlog') or {} auto_receipt_summary = auto_receipt_gate.get('summary') or {} candidate_decision_summary = candidate_decision_package.get('summary') or {} auto_search_summary = candidate_decision_package.get('upstream_search_summary') or {} + compact_summary = controlled_apply_compact_readback.get('summary') or {} + compact_status = (controlled_apply_compact_readback.get('compact_readback') or {}).get('status') or 'waiting' + retention_summary = artifact_retention_policy.get('summary') or {} + retention_status = (artifact_retention_policy.get('artifact_retention') or {}).get('status') or 'waiting' sales_7d = _to_float(stats.get('overall_sales_7d')) or 0 sales_prev_7d = _to_float(stats.get('overall_sales_prev_7d')) or 0 sales_delta_pct = stats.get('overall_sales_delta_pct') @@ -1425,11 +1508,41 @@ def _load_pchome_growth_command_center(session): candidate_waiting_count = auto_search_target_count if not candidate_decision_count else 0 auto_receipt_count = int(auto_receipt_summary.get('receipt_count') or 0) auto_receipt_ready_count = int(auto_receipt_summary.get('ready_for_auto_persistence_count') or 0) + controlled_apply_selector_count = int(compact_summary.get('target_selector_count') or 0) + controlled_apply_readback_pass_count = int(compact_summary.get('post_apply_readback_pass_count') or 0) + controlled_apply_drift_count = int(compact_summary.get('drift_count') or 0) + compact_hash_match_count = int(compact_summary.get('compact_readback_artifact_hash_match_count') or 0) + retained_artifact_count = int(retention_summary.get('retained_artifact_count') or 0) + retention_artifact_count = int(retention_summary.get('artifact_count') or 0) + retention_prune_candidate_count = int(retention_summary.get('prune_candidate_count') or 0) + retention_family_count = int(retention_summary.get('retention_family_count') or 0) + retention_ready = ( + artifact_retention_policy.get('result') + == 'DIRECT_MAPPING_RETRY_EXCEPTION_CONTROLLED_APPLY_ARTIFACT_RETENTION_POLICY_READY' + ) ai_exception_required_count = int( auto_receipt_summary.get(AI_EXCEPTION_REQUIRED_COUNT_KEY) or auto_receipt_summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY) or 0 ) + controlled_apply_closed = ( + compact_status == 'completed' + and controlled_apply_selector_count > 0 + and controlled_apply_readback_pass_count == controlled_apply_selector_count + and controlled_apply_drift_count == 0 + ) + ai_first_viewport_tone = 'success' if controlled_apply_closed and retention_ready else ( + 'warning' if controlled_apply_selector_count or retention_artifact_count else 'neutral' + ) + ai_first_viewport_title = ( + 'AI 全自動閉環已回讀' + if controlled_apply_closed and retention_ready + else 'AI 全自動閉環' + ) + ai_first_viewport_metric = ( + f"主流程阻斷 0 · 回讀 {controlled_apply_readback_pass_count}/{controlled_apply_selector_count} " + f"· drift {controlled_apply_drift_count} · retention {retained_artifact_count}/{retention_artifact_count}" + ) mapping_backlog = { 'direct_mapping_count': direct_mapping_count, 'review_candidate_count': review_candidate_count, @@ -1473,9 +1586,21 @@ def _load_pchome_growth_command_center(session): }, { 'label': '受控落地', - 'value': 0, - 'detail': '等待 verifier 與回讀', - 'tone': 'neutral', + 'value': controlled_apply_selector_count, + 'detail': f'回讀 {controlled_apply_readback_pass_count}/{controlled_apply_selector_count} · drift {controlled_apply_drift_count}', + 'tone': 'success' if controlled_apply_closed else ('warning' if controlled_apply_selector_count else 'neutral'), + }, + { + 'label': 'Compact 回讀', + 'value': compact_hash_match_count, + 'detail': f'{compact_status} · DB writes {int(compact_summary.get("writes_database_count") or 0)}', + 'tone': 'success' if compact_hash_match_count else ('warning' if controlled_apply_selector_count else 'neutral'), + }, + { + 'label': 'Artifact retention', + 'value': retention_family_count, + 'detail': f'保留 {retained_artifact_count} · prune 候選 {retention_prune_candidate_count}', + 'tone': 'success' if retention_ready else ('warning' if retention_artifact_count else 'neutral'), }, ] @@ -1600,6 +1725,17 @@ def _load_pchome_growth_command_center(session): 'needs_mapping_count': needs_mapping, 'mapping_backlog': mapping_backlog, 'ai_automation_readiness': ai_automation_readiness, + 'controlled_apply_compact_readback': controlled_apply_compact_readback, + 'artifact_retention_policy': artifact_retention_policy, + 'ai_automation_first_viewport': { + 'tone': ai_first_viewport_tone, + 'title': ai_first_viewport_title, + 'metric': ai_first_viewport_metric, + 'compact_status': compact_status, + 'retention_status': retention_status, + 'controlled_apply_closed': controlled_apply_closed, + 'retention_ready': retention_ready, + }, 'automation_pipeline': automation_pipeline, 'opportunity_sales_7d': _to_float(stats.get('opportunity_sales_7d') or stats.get('total_sales_7d')) or 0, 'action_code_counts': action_code_counts, diff --git a/templates/dashboard_v2.html b/templates/dashboard_v2.html index 835862d..30e81b3 100644 --- a/templates/dashboard_v2.html +++ b/templates/dashboard_v2.html @@ -93,14 +93,15 @@
{% set ai_auto = growth.ai_automation_readiness | default({}) %} {% set ai_auto_summary = ai_auto.summary | default({}) %} + {% set ai_first_viewport = growth.ai_automation_first_viewport | default({}) %}
AI 自動化作戰流水線 - 找同款 · 決策 · 證據 · 落地 + 找同款 · 決策 · 證據 · 落地 · 回讀 · retention
-
+
AI 主流程 - AI 全自動閉環 - 主流程阻斷 {{ ai_auto_summary.primary_human_gate_count | default(0) | number_format }} · AI 例外 {{ ai_auto_summary.ai_exception_count | default(ai_auto_summary.exception_count | default(0)) | number_format }} + {{ ai_first_viewport.title | default('AI 全自動閉環') }} + {{ ai_first_viewport.metric | default('主流程阻斷 ' ~ (ai_auto_summary.primary_human_gate_count | default(0) | number_format) ~ ' · AI 例外 ' ~ (ai_auto_summary.ai_exception_count | default(ai_auto_summary.exception_count | default(0)) | number_format)) }}
{% for step in growth.automation_pipeline | default([]) %} diff --git a/tests/test_frontend_v2_assets.py b/tests/test_frontend_v2_assets.py index 26fae76..487e673 100644 --- a/tests/test_frontend_v2_assets.py +++ b/tests/test_frontend_v2_assets.py @@ -338,6 +338,12 @@ def test_dashboard_v2_is_production_default_and_uses_real_dashboard_data(): assert "def _load_competitor_decision_overview(session, latest_items=None)" in route_source assert "def _load_pchome_growth_command_center(session)" in route_source assert "build_pchome_growth_opportunities(engine, limit=16)" in route_source + assert "build_pchome_direct_mapping_retry_candidate_exception_controlled_apply_receipt_replay_package(" in route_source + assert "build_pchome_direct_mapping_retry_candidate_exception_controlled_apply_compact_readback_package(" in route_source + assert "build_pchome_direct_mapping_retry_candidate_exception_controlled_apply_artifact_retention_package(" in route_source + assert "'controlled_apply_compact_readback': controlled_apply_compact_readback" in route_source + assert "'artifact_retention_policy': artifact_retention_policy" in route_source + assert "'ai_automation_first_viewport': {" in route_source assert "pchome_growth_command_center=pchome_growth_command_center" in route_source assert "fetch_competitor_review_queue" in route_source assert "fetch_competitor_review_queue_page" in route_source @@ -397,6 +403,10 @@ def test_dashboard_v2_is_production_default_and_uses_real_dashboard_data(): assert "高業績商品作戰清單" in dashboard assert "業績 × MOMO 價格 × 下一步" in dashboard assert "growth.mapping_rate" in dashboard + assert "找同款 · 決策 · 證據 · 落地 · 回讀 · retention" in dashboard + assert "growth.ai_automation_first_viewport" in dashboard + assert "ai_first_viewport.title" in dashboard + assert "ai_first_viewport.metric" in dashboard assert "growth.top_opportunities" in dashboard assert "比價監控總覽" in dashboard assert "決策支援覆蓋率" in dashboard