diff --git a/agent99-control-plane.ps1 b/agent99-control-plane.ps1 index 0b8f79547..fedc1d480 100644 --- a/agent99-control-plane.ps1 +++ b/agent99-control-plane.ps1 @@ -797,11 +797,11 @@ function Get-AgentIncidentCardModel { $coordinatorVerified = Convert-AgentBool (Get-AgentObjectValue $Data "coordinatorVerified" $false) $rebootSloClaimed = Convert-AgentBool (Get-AgentObjectValue $Data "rebootSloClaimed" $false) $verification = if ($coordinatorVerified) { - if ($rebootSloClaimed) { "協調器與 fresh reboot SLO verifier 均已通過。" } else { "服務協調器已通過;本輪不是 fresh reboot drill,不宣稱全主機 SLA。" } + if ($rebootSloClaimed) { "協調器與全主機重啟 SLO verifier 均已通過。" } else { "服務協調器已通過;本輪不是新的全主機重啟演練,不宣稱 10 分鐘 SLA。" } } else { "協調器尚未通過,事件保持開啟。" } - $action = if ($completed) { "完成獨立驗證並寫回 incident/KM receipt。" } else { "持續處理失敗 phase,維持 maintenance fallback。" } + $action = if ($completed) { "完成獨立驗證並寫入事件與知識紀錄。" } else { "持續處理失敗階段,維持系統維護頁。" } $phases = if ($Data -and $Data.PSObject.Properties["phases"]) { @($Data.phases) } else { @() } $visualKind = "recovery" } elseif ($EventType -match "^performance_") { @@ -1172,7 +1172,7 @@ function New-AgentTelegramCardImage { $graphics.FillRectangle($bgBrush, 0, 0, $width, $height) $graphics.FillRectangle($panelBrush, 36, 34, $width - 72, $height - 68) $graphics.FillRectangle($accentBrush, 36, 34, 14, $height - 68) - $graphics.DrawString("Agent99 事故狀態 / INCIDENT STATUS", $titleFont, $textBrush, 72, 54) + $graphics.DrawString("Agent99 事故狀態", $titleFont, $textBrush, 72, 54) $graphics.DrawString($card.lifecycleLabel, $statusFont, $accentBrush, 72, 96) $graphics.DrawString(("$($card.severityLabel) | $($card.incidentId) | 發生 $($card.occurrences) 次"), $labelFont, $mutedBrush, 72, 137) $graphics.DrawString($card.title, $titleFont, $textBrush, [System.Drawing.RectangleF]::new(72, 176, 1048, 46)) @@ -1244,8 +1244,18 @@ function New-AgentTelegramCardImage { $phaseIndex += 1 $passed = [bool]([string]$phase.status -eq "ok") $phaseBrush = if ($passed) { $goodBrush } else { $badBrush } + $phaseLabel = switch ([string]$phase.name) { + "vm_start" { "VMware" } + "host_reachability" { "主機連線" } + "host112_guest_readiness" { "Kali 112" } + "harbor_registry" { "Harbor" } + "k3s_workloads" { "K3s" } + "public_routes" { "公開網站" } + "full_sop_scorecard" { "Full SOP" } + default { Limit-AgentTextLine ([string]$phase.name) 18 } + } $graphics.FillRectangle($phaseBrush, $x, 620, ($phaseWidth - 10), 12) - $graphics.DrawString((Limit-AgentTextLine ([string]$phase.name) 18), $smallFont, $textBrush, $x, 643) + $graphics.DrawString($phaseLabel, $smallFont, $textBrush, $x, 643) $graphics.DrawString($(if ($passed) { "PASS" } else { "OPEN" }), $smallFont, $phaseBrush, $x, 668) } } else { @@ -1262,7 +1272,7 @@ function New-AgentTelegramCardImage { } $graphics.DrawString(("下一次更新:" + $card.nextUpdate), $smallFont, $mutedBrush, [System.Drawing.RectangleF]::new(72, 698, 850, 30)) - $graphics.DrawString((Get-Date -Format "yyyy-MM-dd HH:mm:ss zzz"), $smallFont, $mutedBrush, 930, 704) + $graphics.DrawString((Get-Date -Format "yyyy-MM-dd HH:mm:ss zzz"), $smallFont, $mutedBrush, 860, 704) $bitmap.Save($imagePath, [System.Drawing.Imaging.ImageFormat]::Png) $graphics.Dispose() diff --git a/apps/api/tests/test_agent99_transport_recovery_deploy_contract.py b/apps/api/tests/test_agent99_transport_recovery_deploy_contract.py index 1e93e0638..1470ce15b 100644 --- a/apps/api/tests/test_agent99_transport_recovery_deploy_contract.py +++ b/apps/api/tests/test_agent99_transport_recovery_deploy_contract.py @@ -51,8 +51,8 @@ def test_agent99_recovery_completion_has_human_lifecycle_receipt() -> None: assert '$EventType -in @("recovery_slo_result")' in source assert 'schemaVersion = "agent99_incident_card_v5"' in source assert '主機與服務已通過恢復驗證' in source - assert '本輪不是 fresh reboot drill,不宣稱全主機 SLA' in source - assert 'Agent99 事故狀態 / INCIDENT STATUS' in source + assert '本輪不是新的全主機重啟演練,不宣稱 10 分鐘 SLA' in source + assert 'Agent99 事故狀態' in source assert '使用者影響' in source assert 'Agent99 實際動作' in source assert '獨立驗證' in source @@ -73,6 +73,8 @@ def test_agent99_telegram_v5_hides_raw_evidence_and_has_operator_fields() -> Non assert "kmPath" not in formatter assert "verifierName=" not in formatter assert 'messageFormat = "incident_card_zh_tw_v5"' in source + for phase_label in ("VMware", "主機連線", "Kali 112", "公開網站", "Full SOP"): + assert f'{{ "{phase_label}" }}' in source def test_agent99_v5_formatter_synthetic_gate_loads_model_dependencies() -> None: