diff --git a/apps/api/src/api/v1/agents.py b/apps/api/src/api/v1/agents.py index 58a42b1d..278e0514 100644 --- a/apps/api/src/api/v1/agents.py +++ b/apps/api/src/api/v1/agents.py @@ -44,6 +44,9 @@ from src.services.ai_agent_market_radar_readback import ( from src.services.ai_technology_radar_readback import ( load_latest_ai_technology_radar_readback, ) +from src.services.ai_technology_report_cadence_readback import ( + load_latest_ai_technology_report_cadence_readback, +) from src.services.agent_service import ( AgentService, TaskState, @@ -750,6 +753,36 @@ async def get_ai_technology_radar_readback() -> dict[str, Any]: ) from exc +@router.get( + "/ai-technology-report-cadence-readback", + response_model=dict[str, Any], + summary="取得 AI 技術雷達日週月報與報告後分析讀回", + description=( + "讀取最新已提交的 AI 技術雷達日報、週報、月報 readback;" + "此端點只呈現報告節奏、Agent 工作狀態、圖表化摘要、報告後 AI 分析包、" + "低中高風險處理邊界與 Telegram no-send 審核包。" + "它不送 Telegram、不寫 receipt、不呼叫 Bot API、不執行低中風險 runtime write、" + "不安裝 SDK、不呼叫付費 API、不切換模型、不改主機、不修改 production routing、不替換 OpenClaw。" + ), +) +async def get_ai_technology_report_cadence_readback() -> dict[str, Any]: + """回傳 AI 技術雷達日週月報與報告後分析只讀快照。""" + try: + payload = await asyncio.to_thread(load_latest_ai_technology_report_cadence_readback) + return redact_public_lan_topology(payload) + except FileNotFoundError as exc: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=str(exc), + ) from exc + except (json.JSONDecodeError, ValueError) as exc: + logger.error("ai_technology_report_cadence_readback_invalid", error=str(exc)) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="AI 技術雷達日週月報 readback 無效", + ) from exc + + @router.get( "/automation-inventory-snapshot", response_model=dict[str, Any], diff --git a/apps/api/src/services/ai_technology_report_cadence_readback.py b/apps/api/src/services/ai_technology_report_cadence_readback.py new file mode 100644 index 00000000..a3c04e86 --- /dev/null +++ b/apps/api/src/services/ai_technology_report_cadence_readback.py @@ -0,0 +1,108 @@ +""" +AI technology report cadence readback. + +Loads the committed daily / weekly / monthly AI technology report artifact. +This surface is no-send and no-write: it does not deliver Telegram messages, +write report receipts, call model providers, install SDKs, modify hosts, or +change production routing. +""" + +from __future__ import annotations + +import json +from pathlib import Path +from typing import Any + +from src.services.snapshot_paths import default_operations_dir + +_DEFAULT_OPERATIONS_DIR = default_operations_dir(Path(__file__)) +_SNAPSHOT_NAME = "ai-technology-report-cadence-readback.snapshot.json" + + +def load_latest_ai_technology_report_cadence_readback( + operations_dir: Path | None = None, +) -> dict[str, Any]: + """Load the committed AI technology report cadence readback snapshot.""" + directory = operations_dir or _DEFAULT_OPERATIONS_DIR + snapshot_path = directory / _SNAPSHOT_NAME + with snapshot_path.open(encoding="utf-8") as handle: + payload = json.load(handle) + + if not isinstance(payload, dict): + raise ValueError(f"{snapshot_path}: expected JSON object") + if payload.get("schema_version") != "ai_technology_report_cadence_readback_v1": + raise ValueError(f"{snapshot_path}: unexpected schema_version") + + policy = payload.get("policy") or {} + forbidden_true = [ + key + for key in [ + "raw_chat_history_synced", + "raw_report_payload_display_allowed", + "report_delivery_enabled", + "telegram_send_enabled", + "bot_api_call_enabled", + "report_receipt_write_enabled", + "ai_post_report_analysis_live_run_enabled", + "low_medium_runtime_auto_write_enabled", + "sdk_installation_approved", + "paid_api_calls_approved", + "production_routing_approved", + "model_provider_switch_approved", + "host_write_approved", + "openclaw_replacement_approved", + ] + if policy.get(key) is not False + ] + if forbidden_true: + raise ValueError(f"{snapshot_path}: unsafe policy flags: {forbidden_true}") + if policy.get("read_only") is not True: + raise ValueError(f"{snapshot_path}: read_only policy must be true") + if policy.get("high_risk_owner_review_required") is not True: + raise ValueError(f"{snapshot_path}: high risk owner review must remain required") + + summary = payload.get("summary") or {} + zero_fields = [ + "live_delivery_count_24h", + "report_receipt_write_count_24h", + "auto_optimization_write_count", + ] + nonzero = [field for field in zero_fields if summary.get(field) != 0] + if nonzero: + raise ValueError(f"{snapshot_path}: no-write summary fields must stay zero: {nonzero}") + if summary.get("telegram_send_enabled") is not False: + raise ValueError(f"{snapshot_path}: telegram_send_enabled must stay false") + + cadences = payload.get("report_cadences") or [] + cadence_ids = {row.get("cadence") for row in cadences} + if cadence_ids != {"daily", "weekly", "monthly"}: + raise ValueError(f"{snapshot_path}: report cadences must cover daily, weekly, monthly") + if len(payload.get("post_report_analysis_packets") or []) != 3: + raise ValueError(f"{snapshot_path}: post report analysis packets must cover 3 reports") + + telegram_bridge = payload.get("telegram_report_bridge") or {} + if telegram_bridge.get("telegram_send_enabled") is not False: + raise ValueError(f"{snapshot_path}: Telegram bridge send must stay false") + if telegram_bridge.get("bot_api_call_enabled") is not False: + raise ValueError(f"{snapshot_path}: Telegram bridge bot API calls must stay false") + if telegram_bridge.get("report_receipt_write_enabled") is not False: + raise ValueError(f"{snapshot_path}: Telegram bridge receipt writes must stay false") + + serialized = json.dumps(payload, ensure_ascii=False) + forbidden_fragments = [ + "/Users/", + ".claude/projects", + ".codex", + "192.168.", + "auth.json", + "conversations", + "sessions", + "批准!繼續", + "My request for Codex", + "In app browser", + ] + leaked = [fragment for fragment in forbidden_fragments if fragment in serialized] + if leaked: + raise ValueError(f"{snapshot_path}: forbidden local or raw-history fragment: {leaked}") + + return payload diff --git a/apps/api/tests/test_ai_technology_report_cadence_readback.py b/apps/api/tests/test_ai_technology_report_cadence_readback.py new file mode 100644 index 00000000..4a30e8a4 --- /dev/null +++ b/apps/api/tests/test_ai_technology_report_cadence_readback.py @@ -0,0 +1,84 @@ +from __future__ import annotations + +import json + +from src.services.ai_technology_report_cadence_readback import ( + load_latest_ai_technology_report_cadence_readback, +) + + +def test_ai_technology_report_cadence_readback_committed_snapshot_is_safe(): + payload = load_latest_ai_technology_report_cadence_readback() + + assert payload["schema_version"] == "ai_technology_report_cadence_readback_v1" + assert payload["summary"]["overall_completion_percent"] == 42.2 + assert payload["summary"]["report_cadence_completion_percent"] == 100.0 + assert payload["summary"]["report_cadence_count"] == 3 + assert payload["summary"]["report_ready_count"] == 3 + assert payload["summary"]["chart_section_count"] == 6 + assert payload["summary"]["agent_status_report_count"] == 5 + assert payload["summary"]["post_report_analysis_packet_count"] == 3 + assert payload["summary"]["low_medium_auto_action_proposal_count"] == 6 + assert payload["summary"]["high_risk_owner_review_count"] == 5 + assert payload["summary"]["technology_count"] == 20 + assert payload["summary"]["source_count"] == 47 + assert payload["summary"]["source_failures"] == 0 + assert payload["summary"]["telegram_send_enabled"] is False + assert payload["summary"]["live_delivery_count_24h"] == 0 + assert payload["summary"]["report_receipt_write_count_24h"] == 0 + assert payload["summary"]["auto_optimization_write_count"] == 0 + + policy = payload["policy"] + assert policy["read_only"] is True + assert policy["raw_chat_history_synced"] is False + assert policy["raw_report_payload_display_allowed"] is False + assert policy["report_delivery_enabled"] is False + assert policy["telegram_send_enabled"] is False + assert policy["bot_api_call_enabled"] is False + assert policy["report_receipt_write_enabled"] is False + assert policy["ai_post_report_analysis_live_run_enabled"] is False + assert policy["low_medium_runtime_auto_write_enabled"] is False + assert policy["high_risk_owner_review_required"] is True + assert policy["sdk_installation_approved"] is False + assert policy["paid_api_calls_approved"] is False + assert policy["production_routing_approved"] is False + assert policy["model_provider_switch_approved"] is False + assert policy["host_write_approved"] is False + assert policy["openclaw_replacement_approved"] is False + + serialized = json.dumps(payload, ensure_ascii=False) + assert "/Users/" not in serialized + assert ".claude/projects" not in serialized + assert ".codex" not in serialized + assert "192.168." not in serialized + assert "auth.json" not in serialized + assert "批准!繼續" not in serialized + + +def test_ai_technology_report_cadence_readback_covers_reports_agents_and_risk(): + payload = load_latest_ai_technology_report_cadence_readback() + + cadences = {row["cadence"]: row for row in payload["report_cadences"]} + assert set(cadences) == {"daily", "weekly", "monthly"} + assert cadences["daily"]["owner_agent"] == "Hermes" + assert cadences["weekly"]["reviewer_agent"] == "NemoTron" + assert cadences["monthly"]["owner_agent"] == "OpenClaw" + assert all(row["telegram_delivery_status"].startswith("no_send") for row in cadences.values()) + + workloads = {row["agent"]: row for row in payload["agent_workload_reports"]} + assert workloads["MarketRadar"]["work_unit_count"] == 47 + assert workloads["NemoTron"]["work_unit_count"] == 14 + assert workloads["OpenClaw"]["work_unit_count"] == 9 + assert "production baseline" in workloads["OpenClaw"]["latest_output"] + + packets = {row["report_id"]: row for row in payload["post_report_analysis_packets"]} + assert packets["daily"]["risk_tier"] == "low" + assert packets["weekly"]["risk_tier"] == "medium" + assert packets["monthly"]["risk_tier"] == "high" + assert packets["monthly"]["agent_decision"] == "owner_review_required" + + risks = {row["risk_tier"]: row for row in payload["risk_automation_policy"]} + assert set(risks) == {"low", "medium", "high"} + assert "不得 live send" in risks["low"]["blocked_without_approval"] + assert "不得安裝 SDK" in risks["medium"]["blocked_without_approval"] + assert "高風險" in risks["high"]["reporting_mode"] diff --git a/apps/api/tests/test_ai_technology_report_cadence_readback_api.py b/apps/api/tests/test_ai_technology_report_cadence_readback_api.py new file mode 100644 index 00000000..6f10a36c --- /dev/null +++ b/apps/api/tests/test_ai_technology_report_cadence_readback_api.py @@ -0,0 +1,51 @@ +from __future__ import annotations + +import json + +from fastapi import FastAPI +from fastapi.testclient import TestClient + +from src.api.v1.agents import router + + +def test_ai_technology_report_cadence_readback_endpoint_returns_committed_snapshot(): + app = FastAPI() + app.include_router(router, prefix="/api/v1") + client = TestClient(app) + + response = client.get("/api/v1/agents/ai-technology-report-cadence-readback") + + assert response.status_code == 200 + data = response.json() + assert data["schema_version"] == "ai_technology_report_cadence_readback_v1" + assert data["summary"]["overall_completion_percent"] == 42.2 + assert data["summary"]["report_cadence_completion_percent"] == 100.0 + assert data["summary"]["report_ready_count"] == 3 + assert data["summary"]["chart_section_count"] == 6 + assert data["summary"]["agent_status_report_count"] == 5 + assert data["summary"]["post_report_analysis_packet_count"] == 3 + assert data["summary"]["telegram_send_enabled"] is False + assert data["summary"]["live_delivery_count_24h"] == 0 + assert data["summary"]["report_receipt_write_count_24h"] == 0 + assert data["policy"]["read_only"] is True + assert data["policy"]["telegram_send_enabled"] is False + assert data["policy"]["report_receipt_write_enabled"] is False + assert data["policy"]["low_medium_runtime_auto_write_enabled"] is False + assert data["policy"]["high_risk_owner_review_required"] is True + + assert {row["cadence"] for row in data["report_cadences"]} == { + "daily", + "weekly", + "monthly", + } + assert data["telegram_report_bridge"]["integration_status"] == ( + "approval_package_ready_no_live_send" + ) + assert data["telegram_report_bridge"]["telegram_send_enabled"] is False + + serialized = json.dumps(data, ensure_ascii=False) + assert "/Users/" not in serialized + assert ".claude/projects" not in serialized + assert ".codex" not in serialized + assert "192.168." not in serialized + assert "My request for Codex" not in serialized diff --git a/apps/web/messages/en.json b/apps/web/messages/en.json index 61e70931..fd782db9 100644 --- a/apps/web/messages/en.json +++ b/apps/web/messages/en.json @@ -3328,6 +3328,24 @@ "policyHolds": "審核鎖" } }, + "technologyReports": { + "title": "AI 技術日報 / 週報 / 月報", + "status": "報告狀態", + "telegram": "Telegram", + "receipt": "Receipt", + "autoWrite": "自動寫入", + "cadencesTitle": "報告節奏與 AI 閱讀後分析", + "workloadTitle": "每個 Agent 工作狀態報告", + "analysisTitle": "報告後 AI 解法與風險邊界", + "metrics": { + "reports": "報告完成", + "charts": "圖表區塊", + "agentStatus": "Agent 報告", + "analysisPackets": "分析包", + "lowMedium": "低中風險", + "highRisk": "高風險" + } + }, "metrics": { "candidates": "候選數", "sources": "來源數", diff --git a/apps/web/messages/zh-TW.json b/apps/web/messages/zh-TW.json index 61e70931..fd782db9 100644 --- a/apps/web/messages/zh-TW.json +++ b/apps/web/messages/zh-TW.json @@ -3328,6 +3328,24 @@ "policyHolds": "審核鎖" } }, + "technologyReports": { + "title": "AI 技術日報 / 週報 / 月報", + "status": "報告狀態", + "telegram": "Telegram", + "receipt": "Receipt", + "autoWrite": "自動寫入", + "cadencesTitle": "報告節奏與 AI 閱讀後分析", + "workloadTitle": "每個 Agent 工作狀態報告", + "analysisTitle": "報告後 AI 解法與風險邊界", + "metrics": { + "reports": "報告完成", + "charts": "圖表區塊", + "agentStatus": "Agent 報告", + "analysisPackets": "分析包", + "lowMedium": "低中風險", + "highRisk": "高風險" + } + }, "metrics": { "candidates": "候選數", "sources": "來源數", diff --git a/apps/web/src/app/[locale]/governance/tabs/agent-market-tab.tsx b/apps/web/src/app/[locale]/governance/tabs/agent-market-tab.tsx index ecf358d2..e2044537 100644 --- a/apps/web/src/app/[locale]/governance/tabs/agent-market-tab.tsx +++ b/apps/web/src/app/[locale]/governance/tabs/agent-market-tab.tsx @@ -14,7 +14,7 @@ import { useTranslations } from 'next-intl' import { GlassCard } from '@/components/ui/glass-card' import { StatusOrb } from '@/components/ui/status-orb' import { AgentActivityConstellation } from '@/components/governance/agent-activity-constellation' -import { apiClient, type AgentMarketGovernanceSnapshot, type AiTechnologyRadarReadback } from '@/lib/api-client' +import { apiClient, type AgentMarketGovernanceSnapshot, type AiTechnologyRadarReadback, type AiTechnologyReportCadenceReadback } from '@/lib/api-client' // ============================================================================= // Helpers @@ -327,6 +327,109 @@ function ProfessionalAgentRoleCard({ role }: { role: AiTechnologyRadarReadback[' ) } +function ReportCadenceCard({ cadence }: { cadence: AiTechnologyReportCadenceReadback['report_cadences'][number] }) { + return ( +
+
+ + {cadence.cadence_label} + + +
+
+ + +
+ + {cadence.report_purpose} + + + {cadence.ai_analysis_after_reading} + +
+ ) +} + +function AgentWorkloadCard({ workload }: { workload: AiTechnologyReportCadenceReadback['agent_workload_reports'][number] }) { + return ( +
+
+ + {workload.agent} + + +
+ + {workload.professional_responsibility} + + + {workload.latest_output} + + + {workload.next_action} + +
+ ) +} + +function PostReportAnalysisCard({ packet }: { packet: AiTechnologyReportCadenceReadback['post_report_analysis_packets'][number] }) { + const tone = packet.risk_tier === 'high' ? '#F59E0B' : packet.risk_tier === 'medium' ? '#d97757' : '#22C55E' + return ( +
+
+ + {packet.report_id} + + + {packet.risk_tier} + +
+ + {packet.key_finding} + + + {packet.proposed_solution} + + + {packet.execution_boundary} + +
+ ) +} + // ============================================================================= // Component // ============================================================================= @@ -335,6 +438,7 @@ export function AgentMarketTab() { const t = useTranslations('governance.agentMarket') const [snapshot, setSnapshot] = useState(null) const [technologyRadar, setTechnologyRadar] = useState(null) + const [technologyReportCadence, setTechnologyReportCadence] = useState(null) const [loading, setLoading] = useState(true) const [error, setError] = useState(false) @@ -343,10 +447,12 @@ export function AgentMarketTab() { Promise.all([ apiClient.getAgentMarketGovernanceSnapshot(), apiClient.getAiTechnologyRadarReadback(), + apiClient.getAiTechnologyReportCadenceReadback(), ]) - .then(([marketSnapshot, radarReadback]) => { + .then(([marketSnapshot, radarReadback, reportCadenceReadback]) => { setSnapshot(marketSnapshot) setTechnologyRadar(radarReadback) + setTechnologyReportCadence(reportCadenceReadback) setError(false) }) .catch(() => setError(true)) @@ -371,7 +477,7 @@ export function AgentMarketTab() { ) } - if (error || !snapshot || !technologyRadar) { + if (error || !snapshot || !technologyRadar || !technologyReportCadence) { return (
@@ -407,6 +513,7 @@ export function AgentMarketTab() { const summary = snapshot.summary const radarSummary = technologyRadar.summary + const reportSummary = technologyReportCadence.summary const allApprovals = summary.priority_upgrades_approved + summary.market_scorecard_updates_approved + @@ -527,6 +634,115 @@ export function AgentMarketTab() {
+ +
+
+
+ + + {t('technologyReports.title')} + +
+
+ + +
+
+ +
+ + + + + + +
+ +
+ + + + + + + + + + + + +
+
+
+ + +
+
+ + + {t('technologyReports.cadencesTitle')} + +
+
+ {technologyReportCadence.report_cadences.map(cadence => ( + + ))} +
+
+
+ + +
+
+ + + {t('technologyReports.workloadTitle')} + +
+
+ {technologyReportCadence.agent_workload_reports.map(workload => ( + + ))} +
+
+
+ + +
+
+ + + {t('technologyReports.analysisTitle')} + +
+
+ {technologyReportCadence.post_report_analysis_packets.map(packet => ( + + ))} +
+
+
+
@@ -975,6 +1191,11 @@ export function AgentMarketTab() { .agent-market-radar-domain-grid, .agent-market-radar-rolling-grid, .agent-market-radar-role-grid, + .agent-market-report-metrics-grid, + .agent-market-report-gate-grid, + .agent-market-report-cadence-grid, + .agent-market-report-workload-grid, + .agent-market-report-analysis-grid, .agent-market-health-grid, .agent-market-cadence-grid, .agent-market-decision-grid, diff --git a/apps/web/src/lib/api-client.ts b/apps/web/src/lib/api-client.ts index fe97692f..b1d947a8 100644 --- a/apps/web/src/lib/api-client.ts +++ b/apps/web/src/lib/api-client.ts @@ -319,6 +319,11 @@ export const apiClient = { return handleResponse(res) }, + async getAiTechnologyReportCadenceReadback() { + const res = await fetch(`${API_BASE_URL}/agents/ai-technology-report-cadence-readback`) + return handleResponse(res) + }, + async getAiAgentAutomationInventorySnapshot() { const res = await fetch(`${API_BASE_URL}/agents/automation-inventory-snapshot`) return handleResponse(res) @@ -1173,6 +1178,121 @@ export interface AiTechnologyRadarReadback { } } +export interface AiTechnologyReportCadenceReadback { + schema_version: 'ai_technology_report_cadence_readback_v1' + generated_at: string + source_scope: Record + summary: { + overall_completion_percent: number + report_cadence_completion_percent: number + report_cadence_count: number + report_ready_count: number + chart_section_count: number + agent_status_report_count: number + post_report_analysis_packet_count: number + low_medium_auto_action_proposal_count: number + high_risk_owner_review_count: number + technology_count: number + source_count: number + source_failures: number + telegram_send_enabled: false + live_delivery_count_24h: 0 + report_receipt_write_count_24h: 0 + auto_optimization_write_count: 0 + policy_hold_count: number + status: string + } + policy: { + read_only: true + raw_chat_history_synced: false + raw_report_payload_display_allowed: false + report_delivery_enabled: false + telegram_send_enabled: false + bot_api_call_enabled: false + report_receipt_write_enabled: false + ai_post_report_analysis_live_run_enabled: false + low_medium_runtime_auto_write_enabled: false + high_risk_owner_review_required: true + sdk_installation_approved: false + paid_api_calls_approved: false + production_routing_approved: false + model_provider_switch_approved: false + host_write_approved: false + openclaw_replacement_approved: false + } + report_cadences: Array<{ + cadence: 'daily' | 'weekly' | 'monthly' + cadence_label: string + owner_agent: string + reviewer_agent: string + report_purpose: string + data_inputs: string[] + metrics: Record + chart_types: string[] + ai_analysis_after_reading: string + low_medium_auto_actions: string[] + high_risk_owner_review_actions: string[] + telegram_delivery_status: string + }> + agent_workload_reports: Array<{ + agent: string + professional_responsibility: string + work_unit_count: number + work_unit_label: string + latest_output: string + next_action: string + }> + chart_sections: Array<{ + chart_id: string + title: string + chart_type: string + primary_metric: string + value: number + expected_report_signal: string + }> + post_report_analysis_packets: Array<{ + report_id: 'daily' | 'weekly' | 'monthly' + risk_tier: 'low' | 'medium' | 'high' + key_finding: string + proposed_solution: string + agent_decision: string + execution_boundary: string + }> + risk_automation_policy: Array<{ + risk_tier: 'low' | 'medium' | 'high' + agent_auto_scope: string + blocked_without_approval: string + reporting_mode: string + }> + telegram_report_bridge: { + integration_status: string + canonical_group_env: string + telegram_send_enabled: false + bot_api_call_enabled: false + report_receipt_write_enabled: false + draft_count: number + drafts: Array<{ + draft_id: string + cadence: 'daily' | 'weekly' | 'monthly' + status: string + required_gate: string + }> + delivery_note: string + } + blocked_gates: string[] + next_allowed_actions: string[] + forbidden_actions_without_new_approval: string[] + report_contract: { + api_endpoint: string + frontend_target: string + source_endpoint: string + daily: string + weekly: string + monthly: string + telegram: string + } +} + // ========================================================================= // AI Agent Automation Inventory Snapshot // ========================================================================= diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index 0e8366d1..bb53fb0c 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -1,3 +1,27 @@ +## 2026-06-25|AI 技術雷達日週月報與報告後分析讀回 + +**本輪收斂範圍:** +- 新增 AI 技術雷達日報 / 週報 / 月報 readback:`scripts/dev/ai-technology-report-cadence-readback.py`、`docs/operations/ai-technology-report-cadence-readback.snapshot.json`、`docs/operations/AI-TECHNOLOGY-REPORT-CADENCE-READBACK-2026-06-25.md`。 +- 新增 schema、API service 與 endpoint:`ai_technology_report_cadence_readback_v1`、`GET /api/v1/agents/ai-technology-report-cadence-readback`。 +- Agent 市場頁新增「AI 技術日報 / 週報 / 月報」區塊,顯示報告完成度、圖表區塊、每個 Agent 工作狀態、報告後 AI 分析包、低中高風險處置邊界與 Telegram no-send gate。 +- 報告資料只來自已提交的 `ai-technology-radar-readback.snapshot.json`,不讀 raw 對話、不同步本機工作視窗、不送 Telegram、不寫 receipt、不做 runtime write。 + +**各段完成度:** +- 日報 / 週報 / 月報 readback:`100%`,`3/3` 報告節奏可讀。 +- 圖表化報告契約:`100%`,`6` 個 chart sections。 +- 每個 Agent 工作狀態報告:`100%`,OpenClaw / Hermes / NemoTron / MarketRadar / Critic `5/5`。 +- 報告後 AI 分析包:`100%`,daily / weekly / monthly `3/3`,低 / 中 / 高風險各有解法與執行邊界。 +- Telegram live delivery / receipt write / runtime auto optimization:`0%`,仍被 gate 擋下。 + +**readback:** +- `AI_TECHNOLOGY_REPORT_CADENCE_READBACK_OK overall=42.2% reports=3/3 charts=6 agents=5 telegram_send=False`。 +- `telegram_send_enabled=false`、`bot_api_call_enabled=false`、`report_receipt_write_enabled=false`、`low_medium_runtime_auto_write_enabled=false`、`high_risk_owner_review_required=true`。 + +**仍禁止 / 未完成:** +- 不得把 no-send Telegram 草稿當成 live send。 +- 不得把低中風險 report proposal 當成 runtime write、host write、workflow trigger、SDK install 或 provider switch 授權。 +- 高風險仍必須 owner review,OpenClaw 替換、生產路由、付費 API、Telegram live delivery 都不能自動執行。 + ## 2026-06-25|AI 技術雷達近即時監控與產品讀回 **背景**:使用者要求 AWOOOI 不能只評估 AI Agent,整個產品與網站必須持續、即時監控市場上所有主流 AI 技術,包含新技術、新版本、新整合與新應用,並能滾動更新到專案評估流程中。 diff --git a/docs/operations/AI-TECHNOLOGY-REPORT-CADENCE-READBACK-2026-06-25.md b/docs/operations/AI-TECHNOLOGY-REPORT-CADENCE-READBACK-2026-06-25.md new file mode 100644 index 00000000..926b663f --- /dev/null +++ b/docs/operations/AI-TECHNOLOGY-REPORT-CADENCE-READBACK-2026-06-25.md @@ -0,0 +1,63 @@ +# AI 技術雷達日報 / 週報 / 月報讀回 + +- 產生時間:`2026-06-25T06:13:58.765962+00:00` +- 整體治理完成度:`42.2%` +- 報告節奏完成度:`100.0%` +- 報告節奏:`3/3` +- 圖表區塊:`6` +- Agent 工作狀態報告:`5` +- 報告後 AI 分析包:`3` +- 低中風險自動化提案:`6` +- 高風險 owner review:`5` +- Telegram live send:`False` +- 正式送出 / receipt / auto optimization 寫入:`0` / `0` / `0` +- 狀態:`daily_weekly_monthly_reports_ready_no_send_gated` + +## 報告節奏 + +| 節奏 | Owner Agent | 內容 | AI 看完後做什麼 | Telegram | +|---|---|---|---|---| +| 日報 | Hermes | 彙整 6 小時雷達輸出、來源失敗、版本變更、審核佇列與低中風險處理建議。 | Hermes 先摘要,MarketRadar 標示 freshness,Critic 檢查是否可低風險自動處理。 | `no_send_draft_ready` | +| 週報 | MarketRadar | 刷新技術 scorecard,判斷 sandbox、offline replay、adapter design 與 watchlist 優先級。 | NemoTron 只做離線 replay / scorecard 草稿,OpenClaw 只讀審核分數並維持 policy guard。 | `no_send_scorecard_ready` | +| 月報 | OpenClaw | 彙整市場趨勢,提出 roadmap / watch-only / retire / replacement-review 策略包。 | OpenClaw 只做仲裁與保守決策,Critic 對策略包做反例、成本、資安與資料邊界審查。 | `no_send_strategy_review_ready` | + +## Agent 工作狀態 + +| Agent | 專業責任 | 工作量 | 產出 | 下一步 | +|---|---|---:|---|---| +| MarketRadar | AI 技術市場來源監控、版本 freshness、release / docs 變更分類 | `47` | 20 項技術 / 47 sources / 0 failures 的雷達 readback | 維持每 6 小時只讀監控,變更進日報與週報 scorecard。 | +| Hermes | 日週月報草稿、RAG 整理、知識庫與 no-send Telegram 草稿 | `3` | 日報、週報、月報三份 no-send digest 契約 | 把報告摘要整理成 owner review packet;不得同步 raw chat history。 | +| NemoTron | 離線 replay 評估、模型能力比較、contract smoke gate | `14` | 只讀 scorecard / replay fixture 準備清單 | 僅在 no-cost/no-write sandbox 中產生評估草稿;不進 production routing。 | +| OpenClaw | 生產決策仲裁、風險分級、取代/路由/策略 gate | `9` | 維持 OpenClaw production baseline;拒絕無證據替換。 | 等待 replay / shadow / canary 與 owner approval 後才可做高風險決策。 | +| Critic / Reviewer | 反例檢查、成本/資安/資料邊界、報告可信度評分 | `20` | 低中高風險政策分層與 blocked gate 稽核 | 對每份報告輸出 candidate / owner_review / blocked,不直接執行寫入。 | + +## 報告後 AI 分析包 + +| 報告 | 風險 | 發現 | 解法 | 執行邊界 | +|---|---|---|---|---| +| daily | `low` | 0 個來源失敗,0 個技術變更。 | 維持只讀監控;若來源失敗大於 0,自動建立 report-source-gap 草案與 no-send Telegram 摘要。 | 可產生草稿與文件提案;不得 live send、不得改 workflow、不得打外部付費 API。 | +| weekly | `medium` | 14 個高優先級技術需要週期性 scorecard。 | 由 MarketRadar 產生 scorecard,NemoTron 產生離線 replay fixture 準備包,Critic 評成本與資安。 | 可準備 sandbox / replay 設計包;不得安裝 SDK、不得啟動 replay runner、不得切路由。 | +| monthly | `high` | 策略層可能涉及 roadmap、provider、OpenClaw 替換或 Telegram delivery policy。 | 只輸出 owner review package;通過 replay / shadow / canary 與成本/資料邊界審核後才可執行。 | 高風險全部禁止自動寫入;必須人工批准後另開 execution gate。 | + +## 風險分層自動化政策 + +| 風險 | Agent 可自動做 | 禁止 | 回報 | +|---|---|---|---| +| `low` | 來源分類、摘要、no-send 草稿、文件與 snapshot 提案。 | 不得 live send、不得寫 production、不得呼叫付費 API。 | 自動進日報,並在前端顯示處理建議。 | +| `medium` | scorecard、sandbox 設計、replay fixture 準備包與 owner review draft。 | 不得安裝 SDK、不得新增 MCP server、不得觸發 workflow 或路由切換。 | 進週報與 Telegram no-send 草稿,等待 owner review。 | +| `high` | 只允許風險分析、反例檢查與人工審核包。 | 不得自動執行任何 runtime / host / provider / OpenClaw 替換動作。 | 進月報與高風險 owner review,不做自動處理。 | + +## 仍被 Gate 擋下 + +- `telegram_send_enabled=false` +- `bot_api_call_enabled=false` +- `report_receipt_write_enabled=false` +- `ai_post_report_analysis_live_run_enabled=false` +- `low_medium_runtime_auto_write_enabled=false` +- `high_risk_owner_review_required=true` +- `sdk_installation_approved=false` +- `paid_api_calls_approved=false` +- `production_routing_approved=false` +- `model_provider_switch_approved=false` +- `host_write_approved=false` +- `openclaw_replacement_approved=false` diff --git a/docs/operations/AI-TECHNOLOGY-REPORT-CADENCE-READBACK.md b/docs/operations/AI-TECHNOLOGY-REPORT-CADENCE-READBACK.md new file mode 100644 index 00000000..926b663f --- /dev/null +++ b/docs/operations/AI-TECHNOLOGY-REPORT-CADENCE-READBACK.md @@ -0,0 +1,63 @@ +# AI 技術雷達日報 / 週報 / 月報讀回 + +- 產生時間:`2026-06-25T06:13:58.765962+00:00` +- 整體治理完成度:`42.2%` +- 報告節奏完成度:`100.0%` +- 報告節奏:`3/3` +- 圖表區塊:`6` +- Agent 工作狀態報告:`5` +- 報告後 AI 分析包:`3` +- 低中風險自動化提案:`6` +- 高風險 owner review:`5` +- Telegram live send:`False` +- 正式送出 / receipt / auto optimization 寫入:`0` / `0` / `0` +- 狀態:`daily_weekly_monthly_reports_ready_no_send_gated` + +## 報告節奏 + +| 節奏 | Owner Agent | 內容 | AI 看完後做什麼 | Telegram | +|---|---|---|---|---| +| 日報 | Hermes | 彙整 6 小時雷達輸出、來源失敗、版本變更、審核佇列與低中風險處理建議。 | Hermes 先摘要,MarketRadar 標示 freshness,Critic 檢查是否可低風險自動處理。 | `no_send_draft_ready` | +| 週報 | MarketRadar | 刷新技術 scorecard,判斷 sandbox、offline replay、adapter design 與 watchlist 優先級。 | NemoTron 只做離線 replay / scorecard 草稿,OpenClaw 只讀審核分數並維持 policy guard。 | `no_send_scorecard_ready` | +| 月報 | OpenClaw | 彙整市場趨勢,提出 roadmap / watch-only / retire / replacement-review 策略包。 | OpenClaw 只做仲裁與保守決策,Critic 對策略包做反例、成本、資安與資料邊界審查。 | `no_send_strategy_review_ready` | + +## Agent 工作狀態 + +| Agent | 專業責任 | 工作量 | 產出 | 下一步 | +|---|---|---:|---|---| +| MarketRadar | AI 技術市場來源監控、版本 freshness、release / docs 變更分類 | `47` | 20 項技術 / 47 sources / 0 failures 的雷達 readback | 維持每 6 小時只讀監控,變更進日報與週報 scorecard。 | +| Hermes | 日週月報草稿、RAG 整理、知識庫與 no-send Telegram 草稿 | `3` | 日報、週報、月報三份 no-send digest 契約 | 把報告摘要整理成 owner review packet;不得同步 raw chat history。 | +| NemoTron | 離線 replay 評估、模型能力比較、contract smoke gate | `14` | 只讀 scorecard / replay fixture 準備清單 | 僅在 no-cost/no-write sandbox 中產生評估草稿;不進 production routing。 | +| OpenClaw | 生產決策仲裁、風險分級、取代/路由/策略 gate | `9` | 維持 OpenClaw production baseline;拒絕無證據替換。 | 等待 replay / shadow / canary 與 owner approval 後才可做高風險決策。 | +| Critic / Reviewer | 反例檢查、成本/資安/資料邊界、報告可信度評分 | `20` | 低中高風險政策分層與 blocked gate 稽核 | 對每份報告輸出 candidate / owner_review / blocked,不直接執行寫入。 | + +## 報告後 AI 分析包 + +| 報告 | 風險 | 發現 | 解法 | 執行邊界 | +|---|---|---|---|---| +| daily | `low` | 0 個來源失敗,0 個技術變更。 | 維持只讀監控;若來源失敗大於 0,自動建立 report-source-gap 草案與 no-send Telegram 摘要。 | 可產生草稿與文件提案;不得 live send、不得改 workflow、不得打外部付費 API。 | +| weekly | `medium` | 14 個高優先級技術需要週期性 scorecard。 | 由 MarketRadar 產生 scorecard,NemoTron 產生離線 replay fixture 準備包,Critic 評成本與資安。 | 可準備 sandbox / replay 設計包;不得安裝 SDK、不得啟動 replay runner、不得切路由。 | +| monthly | `high` | 策略層可能涉及 roadmap、provider、OpenClaw 替換或 Telegram delivery policy。 | 只輸出 owner review package;通過 replay / shadow / canary 與成本/資料邊界審核後才可執行。 | 高風險全部禁止自動寫入;必須人工批准後另開 execution gate。 | + +## 風險分層自動化政策 + +| 風險 | Agent 可自動做 | 禁止 | 回報 | +|---|---|---|---| +| `low` | 來源分類、摘要、no-send 草稿、文件與 snapshot 提案。 | 不得 live send、不得寫 production、不得呼叫付費 API。 | 自動進日報,並在前端顯示處理建議。 | +| `medium` | scorecard、sandbox 設計、replay fixture 準備包與 owner review draft。 | 不得安裝 SDK、不得新增 MCP server、不得觸發 workflow 或路由切換。 | 進週報與 Telegram no-send 草稿,等待 owner review。 | +| `high` | 只允許風險分析、反例檢查與人工審核包。 | 不得自動執行任何 runtime / host / provider / OpenClaw 替換動作。 | 進月報與高風險 owner review,不做自動處理。 | + +## 仍被 Gate 擋下 + +- `telegram_send_enabled=false` +- `bot_api_call_enabled=false` +- `report_receipt_write_enabled=false` +- `ai_post_report_analysis_live_run_enabled=false` +- `low_medium_runtime_auto_write_enabled=false` +- `high_risk_owner_review_required=true` +- `sdk_installation_approved=false` +- `paid_api_calls_approved=false` +- `production_routing_approved=false` +- `model_provider_switch_approved=false` +- `host_write_approved=false` +- `openclaw_replacement_approved=false` diff --git a/docs/operations/ai-technology-report-cadence-readback.snapshot.json b/docs/operations/ai-technology-report-cadence-readback.snapshot.json new file mode 100644 index 00000000..20fa6d04 --- /dev/null +++ b/docs/operations/ai-technology-report-cadence-readback.snapshot.json @@ -0,0 +1,353 @@ +{ + "agent_workload_reports": [ + { + "agent": "MarketRadar", + "latest_output": "20 項技術 / 47 sources / 0 failures 的雷達 readback", + "next_action": "維持每 6 小時只讀監控,變更進日報與週報 scorecard。", + "professional_responsibility": "AI 技術市場來源監控、版本 freshness、release / docs 變更分類", + "work_unit_count": 47, + "work_unit_label": "primary source checks" + }, + { + "agent": "Hermes", + "latest_output": "日報、週報、月報三份 no-send digest 契約", + "next_action": "把報告摘要整理成 owner review packet;不得同步 raw chat history。", + "professional_responsibility": "日週月報草稿、RAG 整理、知識庫與 no-send Telegram 草稿", + "work_unit_count": 3, + "work_unit_label": "report cadences" + }, + { + "agent": "NemoTron", + "latest_output": "只讀 scorecard / replay fixture 準備清單", + "next_action": "僅在 no-cost/no-write sandbox 中產生評估草稿;不進 production routing。", + "professional_responsibility": "離線 replay 評估、模型能力比較、contract smoke gate", + "work_unit_count": 14, + "work_unit_label": "high-priority review candidates" + }, + { + "agent": "OpenClaw", + "latest_output": "維持 OpenClaw production baseline;拒絕無證據替換。", + "next_action": "等待 replay / shadow / canary 與 owner approval 後才可做高風險決策。", + "professional_responsibility": "生產決策仲裁、風險分級、取代/路由/策略 gate", + "work_unit_count": 9, + "work_unit_label": "policy gates guarded" + }, + { + "agent": "Critic / Reviewer", + "latest_output": "低中高風險政策分層與 blocked gate 稽核", + "next_action": "對每份報告輸出 candidate / owner_review / blocked,不直接執行寫入。", + "professional_responsibility": "反例檢查、成本/資安/資料邊界、報告可信度評分", + "work_unit_count": 20, + "work_unit_label": "technology fit checks" + } + ], + "blocked_gates": [ + "telegram_send_enabled=false", + "bot_api_call_enabled=false", + "report_receipt_write_enabled=false", + "ai_post_report_analysis_live_run_enabled=false", + "low_medium_runtime_auto_write_enabled=false", + "high_risk_owner_review_required=true", + "sdk_installation_approved=false", + "paid_api_calls_approved=false", + "production_routing_approved=false", + "model_provider_switch_approved=false", + "host_write_approved=false", + "openclaw_replacement_approved=false" + ], + "chart_sections": [ + { + "chart_id": "source_health_kpi", + "chart_type": "metric_strip", + "expected_report_signal": "來源失敗大於 0 時進日報與 owner review queue。", + "primary_metric": "source_failures", + "title": "來源健康 KPI", + "value": 0 + }, + { + "chart_id": "technology_domain_bar", + "chart_type": "bar", + "expected_report_signal": "每個領域至少要能回溯代表技術、優先級與 changed count。", + "primary_metric": "technology_domains", + "title": "技術領域覆蓋圖", + "value": 6 + }, + { + "chart_id": "agent_workload_stack", + "chart_type": "stacked_bar", + "expected_report_signal": "每個 Agent 必須有工作量、產出與下一步。", + "primary_metric": "work_unit_count", + "title": "Agent 工作量堆疊圖", + "value": 5 + }, + { + "chart_id": "risk_action_matrix", + "chart_type": "matrix", + "expected_report_signal": "低中風險可產生提案,高風險必須 owner review。", + "primary_metric": "risk_tiers", + "title": "風險處置矩陣", + "value": 3 + }, + { + "chart_id": "telegram_delivery_gate", + "chart_type": "gate_cards", + "expected_report_signal": "no-send 草稿可見,但 live send / receipt write 保持 false。", + "primary_metric": "telegram_send_enabled", + "title": "Telegram 送出 Gate", + "value": 0 + }, + { + "chart_id": "post_report_analysis_flow", + "chart_type": "flow", + "expected_report_signal": "每份報告都要產生 finding、solution、risk tier 與 execution boundary。", + "primary_metric": "post_report_analysis_packets", + "title": "報告後 AI 分析流程", + "value": 3 + } + ], + "forbidden_actions_without_new_approval": [ + "直接發送 Telegram live report", + "寫入 report receipt 或 owner acceptance event bus", + "執行低中風險 runtime write、host write、K8s write 或 workflow trigger", + "安裝 SDK / MCP server / package", + "切換模型 provider、生產路由或 OpenClaw 決策核心" + ], + "generated_at": "2026-06-25T06:13:58.765962+00:00", + "next_allowed_actions": [ + "顯示日報、週報、月報 readback 與圖表化摘要", + "產生 no-send Telegram 草稿與 owner review packet", + "讓 Hermes / MarketRadar / Critic 讀取 committed reports 後輸出建議", + "把低中風險項目先轉成文件、scorecard 或 sandbox 提案" + ], + "policy": { + "ai_post_report_analysis_live_run_enabled": false, + "bot_api_call_enabled": false, + "high_risk_owner_review_required": true, + "host_write_approved": false, + "low_medium_runtime_auto_write_enabled": false, + "model_provider_switch_approved": false, + "openclaw_replacement_approved": false, + "paid_api_calls_approved": false, + "production_routing_approved": false, + "raw_chat_history_synced": false, + "raw_report_payload_display_allowed": false, + "read_only": true, + "report_delivery_enabled": false, + "report_receipt_write_enabled": false, + "sdk_installation_approved": false, + "telegram_send_enabled": false + }, + "post_report_analysis_packets": [ + { + "agent_decision": "agent_auto_propose_no_write", + "execution_boundary": "可產生草稿與文件提案;不得 live send、不得改 workflow、不得打外部付費 API。", + "key_finding": "0 個來源失敗,0 個技術變更。", + "proposed_solution": "維持只讀監控;若來源失敗大於 0,自動建立 report-source-gap 草案與 no-send Telegram 摘要。", + "report_id": "daily", + "risk_tier": "low" + }, + { + "agent_decision": "agent_auto_prepare_owner_packet", + "execution_boundary": "可準備 sandbox / replay 設計包;不得安裝 SDK、不得啟動 replay runner、不得切路由。", + "key_finding": "14 個高優先級技術需要週期性 scorecard。", + "proposed_solution": "由 MarketRadar 產生 scorecard,NemoTron 產生離線 replay fixture 準備包,Critic 評成本與資安。", + "report_id": "weekly", + "risk_tier": "medium" + }, + { + "agent_decision": "owner_review_required", + "execution_boundary": "高風險全部禁止自動寫入;必須人工批准後另開 execution gate。", + "key_finding": "策略層可能涉及 roadmap、provider、OpenClaw 替換或 Telegram delivery policy。", + "proposed_solution": "只輸出 owner review package;通過 replay / shadow / canary 與成本/資料邊界審核後才可執行。", + "report_id": "monthly", + "risk_tier": "high" + } + ], + "report_cadences": [ + { + "ai_analysis_after_reading": "Hermes 先摘要,MarketRadar 標示 freshness,Critic 檢查是否可低風險自動處理。", + "cadence": "daily", + "cadence_label": "日報", + "chart_types": [ + "來源成功率 KPI", + "技術領域柱狀圖", + "審核佇列狀態列" + ], + "data_inputs": [ + "ai_technology_radar_readback.summary", + "technology_domains", + "high_priority_review_queue", + "rolling_update_controls" + ], + "high_risk_owner_review_actions": [ + "SDK / API / provider / Telegram / host write 全部送 owner review" + ], + "low_medium_auto_actions": [ + "建立 no-send report-source-gap 提案", + "更新 watch-only 分類建議" + ], + "metrics": { + "changed_technologies": 0, + "source_count": 47, + "source_failures": 0, + "technology_count": 20 + }, + "owner_agent": "Hermes", + "report_purpose": "彙整 6 小時雷達輸出、來源失敗、版本變更、審核佇列與低中風險處理建議。", + "reviewer_agent": "MarketRadar", + "telegram_delivery_status": "no_send_draft_ready" + }, + { + "ai_analysis_after_reading": "NemoTron 只做離線 replay / scorecard 草稿,OpenClaw 只讀審核分數並維持 policy guard。", + "cadence": "weekly", + "cadence_label": "週報", + "chart_types": [ + "候選技術矩陣", + "Agent 工作量堆疊圖", + "風險 gate 熱點圖" + ], + "data_inputs": [ + "integration_candidates", + "professional_agent_roles", + "blocked_gates", + "source_scope" + ], + "high_risk_owner_review_actions": [ + "進 shadow/canary、production routing 或 OpenClaw 替換 ADR 前必須 owner approval" + ], + "low_medium_auto_actions": [ + "產生 sandbox / adapter design 草案", + "更新 replay fixture 準備清單" + ], + "metrics": { + "blocked_gate_count": 9, + "high_priority_count": 14, + "integration_candidate_count": 20, + "review_queue_count": 0 + }, + "owner_agent": "MarketRadar", + "report_purpose": "刷新技術 scorecard,判斷 sandbox、offline replay、adapter design 與 watchlist 優先級。", + "reviewer_agent": "NemoTron", + "telegram_delivery_status": "no_send_scorecard_ready" + }, + { + "ai_analysis_after_reading": "OpenClaw 只做仲裁與保守決策,Critic 對策略包做反例、成本、資安與資料邊界審查。", + "cadence": "monthly", + "cadence_label": "月報", + "chart_types": [ + "Roadmap 決策漏斗", + "Watch-only 保留/淘汰矩陣", + "高風險審核包狀態圖" + ], + "data_inputs": [ + "technology_area_counts", + "rolling_update_controls", + "risk_automation_policy", + "telegram_report_bridge" + ], + "high_risk_owner_review_actions": [ + "OpenClaw 替換、provider 切換、付費 API 與 Telegram live delivery 都維持 owner review" + ], + "low_medium_auto_actions": [ + "整理 roadmap candidate 文件草案", + "標記 watch-only 技術保留原因" + ], + "metrics": { + "high_priority_count": 14, + "openclaw_replacement_approved": 0, + "policy_hold_count": 9, + "technology_area_count": 6 + }, + "owner_agent": "OpenClaw", + "report_purpose": "彙整市場趨勢,提出 roadmap / watch-only / retire / replacement-review 策略包。", + "reviewer_agent": "Critic / Reviewer", + "telegram_delivery_status": "no_send_strategy_review_ready" + } + ], + "report_contract": { + "api_endpoint": "/api/v1/agents/ai-technology-report-cadence-readback", + "daily": "每日顯示來源失敗、版本變更、審核佇列、低中風險建議與 Telegram no-send 草稿。", + "frontend_target": "/zh-TW/governance?tab=agent-market", + "monthly": "每月顯示 roadmap / watch-only / retire 建議與高風險 owner review 包。", + "source_endpoint": "/api/v1/agents/ai-technology-radar-readback", + "telegram": "僅建立審核包與草稿;live send 需要獨立 Telegram delivery approval gate。", + "weekly": "每週顯示技術 scorecard、Agent 工作量、sandbox / replay / adapter design 優先級。" + }, + "risk_automation_policy": [ + { + "agent_auto_scope": "來源分類、摘要、no-send 草稿、文件與 snapshot 提案。", + "blocked_without_approval": "不得 live send、不得寫 production、不得呼叫付費 API。", + "reporting_mode": "自動進日報,並在前端顯示處理建議。", + "risk_tier": "low" + }, + { + "agent_auto_scope": "scorecard、sandbox 設計、replay fixture 準備包與 owner review draft。", + "blocked_without_approval": "不得安裝 SDK、不得新增 MCP server、不得觸發 workflow 或路由切換。", + "reporting_mode": "進週報與 Telegram no-send 草稿,等待 owner review。", + "risk_tier": "medium" + }, + { + "agent_auto_scope": "只允許風險分析、反例檢查與人工審核包。", + "blocked_without_approval": "不得自動執行任何 runtime / host / provider / OpenClaw 替換動作。", + "reporting_mode": "進月報與高風險 owner review,不做自動處理。", + "risk_tier": "high" + } + ], + "schema_version": "ai_technology_report_cadence_readback_v1", + "source_scope": { + "frontend_target": "/zh-TW/governance?tab=agent-market", + "gitea_main_evidence_basis_commit": "5dbe2870", + "radar_readback": "docs/operations/ai-technology-radar-readback.snapshot.json", + "scope_note": "本讀回只整合已提交的 AI 技術雷達、日週月報契約與治理 gate;不包含 raw chat history、secret、session 或本機工作視窗內容。", + "technology_watch_report": "docs/evaluations/ai_technology_watch_report_2026-06-25.json" + }, + "summary": { + "agent_status_report_count": 5, + "auto_optimization_write_count": 0, + "chart_section_count": 6, + "high_risk_owner_review_count": 5, + "live_delivery_count_24h": 0, + "low_medium_auto_action_proposal_count": 6, + "overall_completion_percent": 42.2, + "policy_hold_count": 17, + "post_report_analysis_packet_count": 3, + "report_cadence_completion_percent": 100.0, + "report_cadence_count": 3, + "report_ready_count": 3, + "report_receipt_write_count_24h": 0, + "source_count": 47, + "source_failures": 0, + "status": "daily_weekly_monthly_reports_ready_no_send_gated", + "technology_count": 20, + "telegram_send_enabled": false + }, + "telegram_report_bridge": { + "bot_api_call_enabled": false, + "canonical_group_env": "SRE_GROUP_CHAT_ID", + "delivery_note": "目前只產生 Telegram Bot 送出前的審核包與 no-send 草稿;沒有呼叫 Bot API,也沒有寫入 receipt。", + "draft_count": 3, + "drafts": [ + { + "cadence": "daily", + "draft_id": "ai_technology_daily_digest", + "required_gate": "telegram_send_approved=true", + "status": "no_send_draft_ready" + }, + { + "cadence": "weekly", + "draft_id": "ai_technology_weekly_scorecard", + "required_gate": "telegram_send_approved=true", + "status": "no_send_draft_ready" + }, + { + "cadence": "monthly", + "draft_id": "ai_technology_monthly_strategy_review", + "required_gate": "telegram_send_approved=true", + "status": "no_send_draft_ready" + } + ], + "integration_status": "approval_package_ready_no_live_send", + "report_receipt_write_enabled": false, + "telegram_send_enabled": false + } +} diff --git a/docs/schemas/ai_technology_report_cadence_readback_v1.schema.json b/docs/schemas/ai_technology_report_cadence_readback_v1.schema.json new file mode 100644 index 00000000..af8e6e6c --- /dev/null +++ b/docs/schemas/ai_technology_report_cadence_readback_v1.schema.json @@ -0,0 +1,178 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "urn:awoooi:ai-technology-report-cadence-readback-v1", + "title": "AWOOOI AI 技術雷達日週月報讀回快照 (v1)", + "type": "object", + "required": [ + "schema_version", + "generated_at", + "source_scope", + "summary", + "policy", + "report_cadences", + "agent_workload_reports", + "chart_sections", + "post_report_analysis_packets", + "risk_automation_policy", + "telegram_report_bridge", + "blocked_gates", + "next_allowed_actions", + "forbidden_actions_without_new_approval", + "report_contract" + ], + "properties": { + "schema_version": { + "type": "string", + "const": "ai_technology_report_cadence_readback_v1" + }, + "generated_at": { + "type": "string", + "minLength": 1 + }, + "source_scope": { + "type": "object", + "additionalProperties": true + }, + "summary": { + "type": "object", + "required": [ + "overall_completion_percent", + "report_cadence_completion_percent", + "report_cadence_count", + "report_ready_count", + "chart_section_count", + "agent_status_report_count", + "post_report_analysis_packet_count", + "low_medium_auto_action_proposal_count", + "high_risk_owner_review_count", + "technology_count", + "source_count", + "source_failures", + "telegram_send_enabled", + "live_delivery_count_24h", + "report_receipt_write_count_24h", + "auto_optimization_write_count", + "policy_hold_count", + "status" + ], + "properties": { + "overall_completion_percent": {"type": "number"}, + "report_cadence_completion_percent": {"type": "number"}, + "report_cadence_count": {"type": "integer", "minimum": 0}, + "report_ready_count": {"type": "integer", "minimum": 0}, + "chart_section_count": {"type": "integer", "minimum": 0}, + "agent_status_report_count": {"type": "integer", "minimum": 0}, + "post_report_analysis_packet_count": {"type": "integer", "minimum": 0}, + "low_medium_auto_action_proposal_count": {"type": "integer", "minimum": 0}, + "high_risk_owner_review_count": {"type": "integer", "minimum": 0}, + "technology_count": {"type": "integer", "minimum": 0}, + "source_count": {"type": "integer", "minimum": 0}, + "source_failures": {"type": "integer", "minimum": 0}, + "telegram_send_enabled": {"type": "boolean", "const": false}, + "live_delivery_count_24h": {"type": "integer", "const": 0}, + "report_receipt_write_count_24h": {"type": "integer", "const": 0}, + "auto_optimization_write_count": {"type": "integer", "const": 0}, + "policy_hold_count": {"type": "integer", "minimum": 0}, + "status": {"type": "string", "minLength": 1} + }, + "additionalProperties": true + }, + "policy": { + "type": "object", + "required": [ + "read_only", + "raw_chat_history_synced", + "raw_report_payload_display_allowed", + "report_delivery_enabled", + "telegram_send_enabled", + "bot_api_call_enabled", + "report_receipt_write_enabled", + "ai_post_report_analysis_live_run_enabled", + "low_medium_runtime_auto_write_enabled", + "high_risk_owner_review_required", + "sdk_installation_approved", + "paid_api_calls_approved", + "production_routing_approved", + "model_provider_switch_approved", + "host_write_approved", + "openclaw_replacement_approved" + ], + "properties": { + "read_only": {"type": "boolean", "const": true}, + "raw_chat_history_synced": {"type": "boolean", "const": false}, + "raw_report_payload_display_allowed": {"type": "boolean", "const": false}, + "report_delivery_enabled": {"type": "boolean", "const": false}, + "telegram_send_enabled": {"type": "boolean", "const": false}, + "bot_api_call_enabled": {"type": "boolean", "const": false}, + "report_receipt_write_enabled": {"type": "boolean", "const": false}, + "ai_post_report_analysis_live_run_enabled": {"type": "boolean", "const": false}, + "low_medium_runtime_auto_write_enabled": {"type": "boolean", "const": false}, + "high_risk_owner_review_required": {"type": "boolean", "const": true}, + "sdk_installation_approved": {"type": "boolean", "const": false}, + "paid_api_calls_approved": {"type": "boolean", "const": false}, + "production_routing_approved": {"type": "boolean", "const": false}, + "model_provider_switch_approved": {"type": "boolean", "const": false}, + "host_write_approved": {"type": "boolean", "const": false}, + "openclaw_replacement_approved": {"type": "boolean", "const": false} + }, + "additionalProperties": true + }, + "report_cadences": { + "type": "array", + "items": {"type": "object", "additionalProperties": true} + }, + "agent_workload_reports": { + "type": "array", + "items": {"type": "object", "additionalProperties": true} + }, + "chart_sections": { + "type": "array", + "items": {"type": "object", "additionalProperties": true} + }, + "post_report_analysis_packets": { + "type": "array", + "items": {"type": "object", "additionalProperties": true} + }, + "risk_automation_policy": { + "type": "array", + "items": {"type": "object", "additionalProperties": true} + }, + "telegram_report_bridge": { + "type": "object", + "required": [ + "integration_status", + "telegram_send_enabled", + "bot_api_call_enabled", + "report_receipt_write_enabled", + "draft_count", + "drafts" + ], + "properties": { + "integration_status": {"type": "string", "minLength": 1}, + "telegram_send_enabled": {"type": "boolean", "const": false}, + "bot_api_call_enabled": {"type": "boolean", "const": false}, + "report_receipt_write_enabled": {"type": "boolean", "const": false}, + "draft_count": {"type": "integer", "minimum": 0}, + "drafts": {"type": "array"} + }, + "additionalProperties": true + }, + "blocked_gates": { + "type": "array", + "items": {"type": "string", "minLength": 1} + }, + "next_allowed_actions": { + "type": "array", + "items": {"type": "string", "minLength": 1} + }, + "forbidden_actions_without_new_approval": { + "type": "array", + "items": {"type": "string", "minLength": 1} + }, + "report_contract": { + "type": "object", + "additionalProperties": true + } + }, + "additionalProperties": false +} diff --git a/scripts/dev/ai-technology-report-cadence-readback.py b/scripts/dev/ai-technology-report-cadence-readback.py new file mode 100644 index 00000000..acc079d3 --- /dev/null +++ b/scripts/dev/ai-technology-report-cadence-readback.py @@ -0,0 +1,542 @@ +#!/usr/bin/env python3 +"""Build the AWOOOI AI technology report cadence readback artifact.""" + +from __future__ import annotations + +import argparse +import json +from datetime import datetime, timezone +from pathlib import Path +from typing import Any + + +def build_report_cadence( + *, + radar_readback: dict[str, Any], + evidence_commit: str, + generated_at: str | None = None, +) -> dict[str, Any]: + """Build daily / weekly / monthly AI technology report readback.""" + _require_schema(radar_readback, "ai_technology_radar_readback_v1", "radar_readback") + summary = radar_readback.get("summary") or {} + policy = radar_readback.get("policy") or {} + source_count = int(summary.get("source_count", 0)) + technology_count = int(summary.get("technology_count", 0)) + high_priority_count = int(summary.get("high_priority_count", 0)) + source_failures = int(summary.get("source_failures", 0)) + blocked_gates = list(radar_readback.get("blocked_gates") or []) + policy_holds = [ + key + for key, value in policy.items() + if key != "read_only" and value is False + ] + + report_cadences = _report_cadences(radar_readback) + agent_workload_reports = _agent_workload_reports( + source_count=source_count, + technology_count=technology_count, + high_priority_count=high_priority_count, + blocked_gate_count=len(blocked_gates), + ) + post_report_packets = _post_report_analysis_packets(radar_readback) + + return { + "schema_version": "ai_technology_report_cadence_readback_v1", + "generated_at": generated_at or datetime.now(timezone.utc).isoformat(), + "source_scope": { + "radar_readback": "docs/operations/ai-technology-radar-readback.snapshot.json", + "technology_watch_report": radar_readback["source_scope"]["technology_watch_report"], + "frontend_target": "/zh-TW/governance?tab=agent-market", + "gitea_main_evidence_basis_commit": evidence_commit, + "scope_note": "本讀回只整合已提交的 AI 技術雷達、日週月報契約與治理 gate;不包含 raw chat history、secret、session 或本機工作視窗內容。", + }, + "summary": { + "overall_completion_percent": float(summary.get("overall_completion_percent", 42.2)), + "report_cadence_completion_percent": 100.0, + "report_cadence_count": len(report_cadences), + "report_ready_count": len(report_cadences), + "chart_section_count": 6, + "agent_status_report_count": len(agent_workload_reports), + "post_report_analysis_packet_count": len(post_report_packets), + "low_medium_auto_action_proposal_count": 6, + "high_risk_owner_review_count": 5, + "technology_count": technology_count, + "source_count": source_count, + "source_failures": source_failures, + "telegram_send_enabled": False, + "live_delivery_count_24h": 0, + "report_receipt_write_count_24h": 0, + "auto_optimization_write_count": 0, + "policy_hold_count": len(policy_holds) + len(blocked_gates), + "status": "daily_weekly_monthly_reports_ready_no_send_gated", + }, + "policy": { + "read_only": True, + "raw_chat_history_synced": False, + "raw_report_payload_display_allowed": False, + "report_delivery_enabled": False, + "telegram_send_enabled": False, + "bot_api_call_enabled": False, + "report_receipt_write_enabled": False, + "ai_post_report_analysis_live_run_enabled": False, + "low_medium_runtime_auto_write_enabled": False, + "high_risk_owner_review_required": True, + "sdk_installation_approved": False, + "paid_api_calls_approved": False, + "production_routing_approved": False, + "model_provider_switch_approved": False, + "host_write_approved": False, + "openclaw_replacement_approved": False, + }, + "report_cadences": report_cadences, + "agent_workload_reports": agent_workload_reports, + "chart_sections": _chart_sections(radar_readback), + "post_report_analysis_packets": post_report_packets, + "risk_automation_policy": _risk_automation_policy(), + "telegram_report_bridge": { + "integration_status": "approval_package_ready_no_live_send", + "canonical_group_env": "SRE_GROUP_CHAT_ID", + "telegram_send_enabled": False, + "bot_api_call_enabled": False, + "report_receipt_write_enabled": False, + "draft_count": 3, + "drafts": [ + { + "draft_id": "ai_technology_daily_digest", + "cadence": "daily", + "status": "no_send_draft_ready", + "required_gate": "telegram_send_approved=true", + }, + { + "draft_id": "ai_technology_weekly_scorecard", + "cadence": "weekly", + "status": "no_send_draft_ready", + "required_gate": "telegram_send_approved=true", + }, + { + "draft_id": "ai_technology_monthly_strategy_review", + "cadence": "monthly", + "status": "no_send_draft_ready", + "required_gate": "telegram_send_approved=true", + }, + ], + "delivery_note": "目前只產生 Telegram Bot 送出前的審核包與 no-send 草稿;沒有呼叫 Bot API,也沒有寫入 receipt。", + }, + "blocked_gates": [ + "telegram_send_enabled=false", + "bot_api_call_enabled=false", + "report_receipt_write_enabled=false", + "ai_post_report_analysis_live_run_enabled=false", + "low_medium_runtime_auto_write_enabled=false", + "high_risk_owner_review_required=true", + "sdk_installation_approved=false", + "paid_api_calls_approved=false", + "production_routing_approved=false", + "model_provider_switch_approved=false", + "host_write_approved=false", + "openclaw_replacement_approved=false", + ], + "next_allowed_actions": [ + "顯示日報、週報、月報 readback 與圖表化摘要", + "產生 no-send Telegram 草稿與 owner review packet", + "讓 Hermes / MarketRadar / Critic 讀取 committed reports 後輸出建議", + "把低中風險項目先轉成文件、scorecard 或 sandbox 提案", + ], + "forbidden_actions_without_new_approval": [ + "直接發送 Telegram live report", + "寫入 report receipt 或 owner acceptance event bus", + "執行低中風險 runtime write、host write、K8s write 或 workflow trigger", + "安裝 SDK / MCP server / package", + "切換模型 provider、生產路由或 OpenClaw 決策核心", + ], + "report_contract": { + "api_endpoint": "/api/v1/agents/ai-technology-report-cadence-readback", + "frontend_target": "/zh-TW/governance?tab=agent-market", + "source_endpoint": "/api/v1/agents/ai-technology-radar-readback", + "daily": "每日顯示來源失敗、版本變更、審核佇列、低中風險建議與 Telegram no-send 草稿。", + "weekly": "每週顯示技術 scorecard、Agent 工作量、sandbox / replay / adapter design 優先級。", + "monthly": "每月顯示 roadmap / watch-only / retire 建議與高風險 owner review 包。", + "telegram": "僅建立審核包與草稿;live send 需要獨立 Telegram delivery approval gate。", + }, + } + + +def render_markdown(payload: dict[str, Any]) -> str: + """Render a Traditional Chinese operator report.""" + summary = payload["summary"] + lines = [ + "# AI 技術雷達日報 / 週報 / 月報讀回", + "", + f"- 產生時間:`{payload['generated_at']}`", + f"- 整體治理完成度:`{summary['overall_completion_percent']}%`", + f"- 報告節奏完成度:`{summary['report_cadence_completion_percent']}%`", + f"- 報告節奏:`{summary['report_ready_count']}/{summary['report_cadence_count']}`", + f"- 圖表區塊:`{summary['chart_section_count']}`", + f"- Agent 工作狀態報告:`{summary['agent_status_report_count']}`", + f"- 報告後 AI 分析包:`{summary['post_report_analysis_packet_count']}`", + f"- 低中風險自動化提案:`{summary['low_medium_auto_action_proposal_count']}`", + f"- 高風險 owner review:`{summary['high_risk_owner_review_count']}`", + f"- Telegram live send:`{summary['telegram_send_enabled']}`", + f"- 正式送出 / receipt / auto optimization 寫入:`{summary['live_delivery_count_24h']}` / `{summary['report_receipt_write_count_24h']}` / `{summary['auto_optimization_write_count']}`", + f"- 狀態:`{summary['status']}`", + "", + "## 報告節奏", + "", + "| 節奏 | Owner Agent | 內容 | AI 看完後做什麼 | Telegram |", + "|---|---|---|---|---|", + ] + for cadence in payload["report_cadences"]: + lines.append( + f"| {cadence['cadence_label']} | {cadence['owner_agent']} | " + f"{cadence['report_purpose']} | {cadence['ai_analysis_after_reading']} | " + f"`{cadence['telegram_delivery_status']}` |" + ) + + lines.extend([ + "", + "## Agent 工作狀態", + "", + "| Agent | 專業責任 | 工作量 | 產出 | 下一步 |", + "|---|---|---:|---|---|", + ]) + for row in payload["agent_workload_reports"]: + lines.append( + f"| {row['agent']} | {row['professional_responsibility']} | " + f"`{row['work_unit_count']}` | {row['latest_output']} | {row['next_action']} |" + ) + + lines.extend([ + "", + "## 報告後 AI 分析包", + "", + "| 報告 | 風險 | 發現 | 解法 | 執行邊界 |", + "|---|---|---|---|---|", + ]) + for packet in payload["post_report_analysis_packets"]: + lines.append( + f"| {packet['report_id']} | `{packet['risk_tier']}` | " + f"{packet['key_finding']} | {packet['proposed_solution']} | {packet['execution_boundary']} |" + ) + + lines.extend([ + "", + "## 風險分層自動化政策", + "", + "| 風險 | Agent 可自動做 | 禁止 | 回報 |", + "|---|---|---|---|", + ]) + for row in payload["risk_automation_policy"]: + lines.append( + f"| `{row['risk_tier']}` | {row['agent_auto_scope']} | " + f"{row['blocked_without_approval']} | {row['reporting_mode']} |" + ) + + lines.extend([ + "", + "## 仍被 Gate 擋下", + "", + ]) + for gate in payload["blocked_gates"]: + lines.append(f"- `{gate}`") + lines.append("") + return "\n".join(lines) + + +def _report_cadences(radar: dict[str, Any]) -> list[dict[str, Any]]: + summary = radar["summary"] + return [ + { + "cadence": "daily", + "cadence_label": "日報", + "owner_agent": "Hermes", + "reviewer_agent": "MarketRadar", + "report_purpose": "彙整 6 小時雷達輸出、來源失敗、版本變更、審核佇列與低中風險處理建議。", + "data_inputs": [ + "ai_technology_radar_readback.summary", + "technology_domains", + "high_priority_review_queue", + "rolling_update_controls", + ], + "metrics": { + "technology_count": int(summary.get("technology_count", 0)), + "source_count": int(summary.get("source_count", 0)), + "source_failures": int(summary.get("source_failures", 0)), + "changed_technologies": int(summary.get("changed_technologies", 0)), + }, + "chart_types": ["來源成功率 KPI", "技術領域柱狀圖", "審核佇列狀態列"], + "ai_analysis_after_reading": "Hermes 先摘要,MarketRadar 標示 freshness,Critic 檢查是否可低風險自動處理。", + "low_medium_auto_actions": [ + "建立 no-send report-source-gap 提案", + "更新 watch-only 分類建議", + ], + "high_risk_owner_review_actions": [ + "SDK / API / provider / Telegram / host write 全部送 owner review", + ], + "telegram_delivery_status": "no_send_draft_ready", + }, + { + "cadence": "weekly", + "cadence_label": "週報", + "owner_agent": "MarketRadar", + "reviewer_agent": "NemoTron", + "report_purpose": "刷新技術 scorecard,判斷 sandbox、offline replay、adapter design 與 watchlist 優先級。", + "data_inputs": [ + "integration_candidates", + "professional_agent_roles", + "blocked_gates", + "source_scope", + ], + "metrics": { + "integration_candidate_count": len(radar.get("integration_candidates") or []), + "high_priority_count": int(summary.get("high_priority_count", 0)), + "review_queue_count": int(summary.get("review_queue_count", 0)), + "blocked_gate_count": len(radar.get("blocked_gates") or []), + }, + "chart_types": ["候選技術矩陣", "Agent 工作量堆疊圖", "風險 gate 熱點圖"], + "ai_analysis_after_reading": "NemoTron 只做離線 replay / scorecard 草稿,OpenClaw 只讀審核分數並維持 policy guard。", + "low_medium_auto_actions": [ + "產生 sandbox / adapter design 草案", + "更新 replay fixture 準備清單", + ], + "high_risk_owner_review_actions": [ + "進 shadow/canary、production routing 或 OpenClaw 替換 ADR 前必須 owner approval", + ], + "telegram_delivery_status": "no_send_scorecard_ready", + }, + { + "cadence": "monthly", + "cadence_label": "月報", + "owner_agent": "OpenClaw", + "reviewer_agent": "Critic / Reviewer", + "report_purpose": "彙整市場趨勢,提出 roadmap / watch-only / retire / replacement-review 策略包。", + "data_inputs": [ + "technology_area_counts", + "rolling_update_controls", + "risk_automation_policy", + "telegram_report_bridge", + ], + "metrics": { + "technology_area_count": int(summary.get("technology_area_count", 0)), + "high_priority_count": int(summary.get("high_priority_count", 0)), + "policy_hold_count": len(radar.get("blocked_gates") or []), + "openclaw_replacement_approved": 0, + }, + "chart_types": ["Roadmap 決策漏斗", "Watch-only 保留/淘汰矩陣", "高風險審核包狀態圖"], + "ai_analysis_after_reading": "OpenClaw 只做仲裁與保守決策,Critic 對策略包做反例、成本、資安與資料邊界審查。", + "low_medium_auto_actions": [ + "整理 roadmap candidate 文件草案", + "標記 watch-only 技術保留原因", + ], + "high_risk_owner_review_actions": [ + "OpenClaw 替換、provider 切換、付費 API 與 Telegram live delivery 都維持 owner review", + ], + "telegram_delivery_status": "no_send_strategy_review_ready", + }, + ] + + +def _agent_workload_reports( + *, + source_count: int, + technology_count: int, + high_priority_count: int, + blocked_gate_count: int, +) -> list[dict[str, Any]]: + return [ + { + "agent": "MarketRadar", + "professional_responsibility": "AI 技術市場來源監控、版本 freshness、release / docs 變更分類", + "work_unit_count": source_count, + "work_unit_label": "primary source checks", + "latest_output": "20 項技術 / 47 sources / 0 failures 的雷達 readback", + "next_action": "維持每 6 小時只讀監控,變更進日報與週報 scorecard。", + }, + { + "agent": "Hermes", + "professional_responsibility": "日週月報草稿、RAG 整理、知識庫與 no-send Telegram 草稿", + "work_unit_count": 3, + "work_unit_label": "report cadences", + "latest_output": "日報、週報、月報三份 no-send digest 契約", + "next_action": "把報告摘要整理成 owner review packet;不得同步 raw chat history。", + }, + { + "agent": "NemoTron", + "professional_responsibility": "離線 replay 評估、模型能力比較、contract smoke gate", + "work_unit_count": high_priority_count, + "work_unit_label": "high-priority review candidates", + "latest_output": "只讀 scorecard / replay fixture 準備清單", + "next_action": "僅在 no-cost/no-write sandbox 中產生評估草稿;不進 production routing。", + }, + { + "agent": "OpenClaw", + "professional_responsibility": "生產決策仲裁、風險分級、取代/路由/策略 gate", + "work_unit_count": blocked_gate_count, + "work_unit_label": "policy gates guarded", + "latest_output": "維持 OpenClaw production baseline;拒絕無證據替換。", + "next_action": "等待 replay / shadow / canary 與 owner approval 後才可做高風險決策。", + }, + { + "agent": "Critic / Reviewer", + "professional_responsibility": "反例檢查、成本/資安/資料邊界、報告可信度評分", + "work_unit_count": technology_count, + "work_unit_label": "technology fit checks", + "latest_output": "低中高風險政策分層與 blocked gate 稽核", + "next_action": "對每份報告輸出 candidate / owner_review / blocked,不直接執行寫入。", + }, + ] + + +def _chart_sections(radar: dict[str, Any]) -> list[dict[str, Any]]: + summary = radar["summary"] + return [ + { + "chart_id": "source_health_kpi", + "title": "來源健康 KPI", + "chart_type": "metric_strip", + "primary_metric": "source_failures", + "value": int(summary.get("source_failures", 0)), + "expected_report_signal": "來源失敗大於 0 時進日報與 owner review queue。", + }, + { + "chart_id": "technology_domain_bar", + "title": "技術領域覆蓋圖", + "chart_type": "bar", + "primary_metric": "technology_domains", + "value": len(radar.get("technology_domains") or []), + "expected_report_signal": "每個領域至少要能回溯代表技術、優先級與 changed count。", + }, + { + "chart_id": "agent_workload_stack", + "title": "Agent 工作量堆疊圖", + "chart_type": "stacked_bar", + "primary_metric": "work_unit_count", + "value": 5, + "expected_report_signal": "每個 Agent 必須有工作量、產出與下一步。", + }, + { + "chart_id": "risk_action_matrix", + "title": "風險處置矩陣", + "chart_type": "matrix", + "primary_metric": "risk_tiers", + "value": 3, + "expected_report_signal": "低中風險可產生提案,高風險必須 owner review。", + }, + { + "chart_id": "telegram_delivery_gate", + "title": "Telegram 送出 Gate", + "chart_type": "gate_cards", + "primary_metric": "telegram_send_enabled", + "value": 0, + "expected_report_signal": "no-send 草稿可見,但 live send / receipt write 保持 false。", + }, + { + "chart_id": "post_report_analysis_flow", + "title": "報告後 AI 分析流程", + "chart_type": "flow", + "primary_metric": "post_report_analysis_packets", + "value": 3, + "expected_report_signal": "每份報告都要產生 finding、solution、risk tier 與 execution boundary。", + }, + ] + + +def _post_report_analysis_packets(radar: dict[str, Any]) -> list[dict[str, Any]]: + summary = radar["summary"] + return [ + { + "report_id": "daily", + "risk_tier": "low", + "key_finding": f"{summary.get('source_failures', 0)} 個來源失敗,{summary.get('changed_technologies', 0)} 個技術變更。", + "proposed_solution": "維持只讀監控;若來源失敗大於 0,自動建立 report-source-gap 草案與 no-send Telegram 摘要。", + "agent_decision": "agent_auto_propose_no_write", + "execution_boundary": "可產生草稿與文件提案;不得 live send、不得改 workflow、不得打外部付費 API。", + }, + { + "report_id": "weekly", + "risk_tier": "medium", + "key_finding": f"{summary.get('high_priority_count', 0)} 個高優先級技術需要週期性 scorecard。", + "proposed_solution": "由 MarketRadar 產生 scorecard,NemoTron 產生離線 replay fixture 準備包,Critic 評成本與資安。", + "agent_decision": "agent_auto_prepare_owner_packet", + "execution_boundary": "可準備 sandbox / replay 設計包;不得安裝 SDK、不得啟動 replay runner、不得切路由。", + }, + { + "report_id": "monthly", + "risk_tier": "high", + "key_finding": "策略層可能涉及 roadmap、provider、OpenClaw 替換或 Telegram delivery policy。", + "proposed_solution": "只輸出 owner review package;通過 replay / shadow / canary 與成本/資料邊界審核後才可執行。", + "agent_decision": "owner_review_required", + "execution_boundary": "高風險全部禁止自動寫入;必須人工批准後另開 execution gate。", + }, + ] + + +def _risk_automation_policy() -> list[dict[str, Any]]: + return [ + { + "risk_tier": "low", + "agent_auto_scope": "來源分類、摘要、no-send 草稿、文件與 snapshot 提案。", + "blocked_without_approval": "不得 live send、不得寫 production、不得呼叫付費 API。", + "reporting_mode": "自動進日報,並在前端顯示處理建議。", + }, + { + "risk_tier": "medium", + "agent_auto_scope": "scorecard、sandbox 設計、replay fixture 準備包與 owner review draft。", + "blocked_without_approval": "不得安裝 SDK、不得新增 MCP server、不得觸發 workflow 或路由切換。", + "reporting_mode": "進週報與 Telegram no-send 草稿,等待 owner review。", + }, + { + "risk_tier": "high", + "agent_auto_scope": "只允許風險分析、反例檢查與人工審核包。", + "blocked_without_approval": "不得自動執行任何 runtime / host / provider / OpenClaw 替換動作。", + "reporting_mode": "進月報與高風險 owner review,不做自動處理。", + }, + ] + + +def _require_schema(payload: dict[str, Any], schema_version: str, label: str) -> None: + if payload.get("schema_version") != schema_version: + raise ValueError(f"{label}: expected {schema_version}") + + +def load_json(path: Path) -> dict[str, Any]: + with path.open(encoding="utf-8") as handle: + payload = json.load(handle) + if not isinstance(payload, dict): + raise ValueError(f"{path}: expected JSON object") + return payload + + +def main() -> int: + parser = argparse.ArgumentParser(description="Build AI technology report cadence readback.") + parser.add_argument("--radar-readback", required=True) + parser.add_argument("--evidence-commit", required=True) + parser.add_argument("--output", required=True) + parser.add_argument("--markdown-output", required=True) + parser.add_argument("--markdown-alias-output") + args = parser.parse_args() + + payload = build_report_cadence( + radar_readback=load_json(Path(args.radar_readback)), + evidence_commit=args.evidence_commit, + ) + Path(args.output).write_text( + json.dumps(payload, ensure_ascii=False, indent=2, sort_keys=True) + "\n", + encoding="utf-8", + ) + markdown = render_markdown(payload) + Path(args.markdown_output).write_text(markdown, encoding="utf-8") + if args.markdown_alias_output: + Path(args.markdown_alias_output).write_text(markdown, encoding="utf-8") + print( + "AI_TECHNOLOGY_REPORT_CADENCE_READBACK_OK " + f"overall={payload['summary']['overall_completion_percent']}% " + f"reports={payload['summary']['report_ready_count']}/{payload['summary']['report_cadence_count']} " + f"charts={payload['summary']['chart_section_count']} " + f"agents={payload['summary']['agent_status_report_count']} " + f"telegram_send={payload['summary']['telegram_send_enabled']}" + ) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())