"""平台 / 运维域通知模板。 覆盖跨域的「平台自己在跑」的流程,收件人既有普通员工也有 IT 管理员: * ``approval.reaper_terminal`` — 飞书审批 reaper 自动关闭超期审批后通知申请人 (``app/scheduled_jobs/feishu_handlers.py``)。 * ``finance.close_reminder`` — 月结关账任务催办,推给本期任务 owner (``app/scheduled_jobs/finance_handlers.py``)。 * ``procurement.reminder.*`` — 采购日常提醒三类:合同到期 / 预算阈值 / 汇联易迟批(``app/scheduled_jobs/procurement_handlers.py``)。三类文案完全 不同,所以拆成三个 key —— 一个 template_key 无法渲染三种语义。历史上它们 共用 ``procurement.reminder``(未注册,故一直只发中文);去重逻辑按 key 家族匹配,老 key 保留在 look-back 列表里避免切换当周重复打扰。 * ``identity_sync.auto_included_manager`` — 因为下属在同步范围内而被自动带入 的 manager,账号在下游系统建好后的告知信 (``app/domains/identity_sync/service.py``)。 * ``identity_sync.unmapped_depts`` / ``.value_map_misses`` / ``.unadopted_ns_depts`` / ``.blocked_backlog`` — 四类「同步跑绿了但有事要人 处理」的告警(``app/domains/identity_sync/alert_notifier.py``)。收件人是该同 步目标的 ``alert_recipient_ids``。明细行留在 ``card_sections`` 里(语言中立 的编码 / 邮箱 / 原因),正文只承载散文,与这四条告警上线时的原文一致。 * ``jira.*`` — Atlassian 离职残留 / 撤销反弹三类治理告警 (``app/scheduled_jobs/jira_handlers.py``)。收件人是 IT 管理员,但管理员里 也有日本同事,所以同样三语;语气按运维告警走,陈述事实 + 明确下一步。 * ``approval_feishu_cancel_desync`` — Helix 撤回成功但飞书审批单没撤掉时给 global_admin 的对账告警(``app/approval/cancel_desync.py``)。channels=[], 只落铃铛,所以正文必须走模板(否则铃铛读的是调用点那份中文)。 * ``access_review.self_attestation`` / ``.overdue`` / ``.overdue_manager`` — 半年度访问自查的三条通知:开跑请你确认 / 你逾期了 / 你下属逾期了。三句完全 不同的话,一个 template_key 只能渲染一句,所以拆成三个 key —— 与 ``procurement.reminder.*`` 当初拆三个 key 是同一个理由。 明细行(``{sample_block}``)由调用点拼装,内容是邮箱 / 产品名 / 次数这类 语言中立的数据,不含散文,才能安全地嵌进任意一种语言的正文里。 """ from __future__ import annotations TEMPLATES: dict[str, dict[str, str]] = { # 年度报告 (Helix Wrapped) — the 12-31 push. Deliberately warm, and the # only celebratory template in this catalog; everything else here is # operational. Sent once per person per year, gated on the recap actually # having content (``is_wrapped_meaningful``). # 助手知识缺口催办 — IT-only. Shipped in #3117 declaring this # ``template_key`` but never registering it, so every recipient got the # producer's hardcoded Chinese regardless of their language (caught by # ``test_every_sent_template_key_is_registered`` 2026-08-24). The # producer now passes the three interpolations as context instead of # pre-formatting the body. # 助手每周回归评测的回退告警 — IT-only(cron assistant.eval_regression)。 # 只在「可比」且下滑超过阈值时才发:一条会误报的告警,几周之内就会变成 # 没人看的告警。context: pass_rate, baseline_pass_rate, drop_pp, # shared_cases, regressed_count, regressed_lines, model_note # # ⚠️ 正文里的两个百分比是 **shared_** 那一对,不是两次运行各自的 headline。 # `drop_pp` 是在「两次都真正判过的那批用例」上算的,而 headline 是各自覆盖 # 多少就算多少 —— 混着印会印出三个对不上的数字,而这封信是管理员周一早上 # 唯一会读的东西。 "assistant.eval_regression": { "subject": "助手评测回退 · 通过率下降 {drop_pp} 个百分点", "subject_en": "Assistant evaluation regression · pass rate down {drop_pp}pp", "subject_ja": "アシスタント評価が後退 · 合格率が {drop_pp} ポイント低下", "body_text": ( "在两次都跑过的 {shared_cases} 条用例上,本周通过率是 {pass_rate}%," "上一次是 {baseline_pass_rate}%,下降了 {drop_pp} 个百分点。\n\n" "{model_note}\n\n" "从通过变成不通过的用例({regressed_count} 条):\n{regressed_lines}\n\n" "打开运行明细可以看到每一条的原始回答、它读到的文档和评委的判词。\n\n" "{cta_line}\n\n" "— Helix IT 平台" ), "body_text_en": ( "Over the {shared_cases} cases both runs scored, this week came in " "at {pass_rate}% against {baseline_pass_rate}% last time — down " "{drop_pp} points.\n\n" "{model_note}\n\n" "Cases that went from passing to failing ({regressed_count}):\n" "{regressed_lines}\n\n" "Open the run to see each reply, the documents it actually read, " "and the reviewer's reasoning.\n\n" "{cta_line}\n\n" "— Helix IT Platform" ), "body_text_ja": ( "両方の実行が採点した {shared_cases} 件のケースでは、今週の合格率は " "{pass_rate}%、前回は {baseline_pass_rate}% で、" "{drop_pp} ポイント低下しました。\n\n" "{model_note}\n\n" "合格から不合格に変わったケース({regressed_count} 件):\n" "{regressed_lines}\n\n" "実行詳細を開くと、各回答・参照した文書・レビューアの判断理由を" "確認できます。\n\n" "{cta_line}\n\n" "— Helix IT プラットフォーム" ), "card_button_label": "查看这次评测", "card_button_label_en": "Open this run", "card_button_label_ja": "この実行を開く", }, "assistant.knowledge_gap_reminder": { "subject": "助手知识缺口待处理(第 {reminder_count} 次提醒)", "subject_en": ( "Assistant knowledge gap still open (reminder #{reminder_count})" ), "subject_ja": ( "アシスタントの知識ギャップが未対応です(通知 {reminder_count} 回目)" ), "body_text": ( "有一个助手回答被标记为知识缺口,还没有处理。\n\n" " · 负责人: {owner_label}\n" " · 提醒次数: 第 {reminder_count} 次\n\n" "原回答摘录:\n{answer_excerpt}\n\n" "请认领并补充知识库;完成后标记解决,误报则标记忽略。\n\n" "{cta_line}\n\n" "— Helix IT 平台" ), "body_text_en": ( "An assistant answer was flagged as a knowledge gap and is still " "open.\n\n" " · Owner: {owner_label}\n" " · Reminder: #{reminder_count}\n\n" "Excerpt of the original answer:\n{answer_excerpt}\n\n" "Please claim it and fill the knowledge base; mark it resolved " "when done, or ignored if it was a false alarm.\n\n" "{cta_line}\n\n" "— Helix IT Platform" ), "body_text_ja": ( "アシスタントの回答が知識ギャップとして記録され、未対応のままです。\n\n" " · 担当: {owner_label}\n" " · 通知回数: {reminder_count} 回目\n\n" "元の回答の抜粋:\n{answer_excerpt}\n\n" "担当を引き受け、ナレッジベースを補完してください。完了後は解決、" "誤検知の場合は無視としてマークしてください。\n\n" "{cta_line}\n\n" "— Helix IT プラットフォーム" ), "card_button_label": "打开助手审计", "card_button_label_en": "Open assistant audit", "card_button_label_ja": "アシスタント監査を開く", }, "wrapped_year_end": { "subject": "🎉 你的 {year} 年度报告来啦", "subject_en": "🎉 Your {year} Helix Wrapped is ready", "subject_ja": "🎉 {year} 年のあなたの Helix Wrapped が公開されました", "body_text": ( "{year} 这一年,你在 Helix 留下了不少足迹。\n\n" "点开看看你的年度回顾 —— 来过多少天、领了什么装备、" "拿到哪些成就徽章,还有属于你的年度关键词。\n\n" "{cta_line}" ), "body_text_en": ( "You left a lot of footprints on Helix in {year}.\n\n" "Open your year in review — the days you showed up, the kit you " "picked up, the badges you earned, and the keyword that turned out " "to describe your year.\n\n" "{cta_line}" ), "body_text_ja": ( "{year} 年、Helix にはあなたの足あとがたくさん残りました。\n\n" "年間振り返りをご覧ください。ログインした日数、受け取った端末、" "獲得したバッジ、そしてあなたの一年を表すキーワードをまとめています。\n\n" "{cta_line}" ), "card_button_label": "看看我的年度报告", "card_button_label_en": "Open my Wrapped", "card_button_label_ja": "年間振り返りを見る", }, # ── 审批 reaper ────────────────────────────────────────────────── "approval.reaper_terminal": { "subject": "[Helix] 您的审批请求已自动关闭", "subject_en": "[Helix] Your approval request was closed automatically", "subject_ja": "[Helix] 承認申請が自動的にクローズされました", "body_text": ( "您于 {submitted_at} 提交的审批请求已被系统自动标记为 " "[b]{new_status}[/b]。\n\n" "原因:{reason}\n\n" "如仍需此审批,请重新提交。\n\n" "{cta_line}\n\n" "— Helix IT 平台" ), "body_text_en": ( "The approval request you submitted on {submitted_at} was " "automatically marked as [b]{new_status}[/b] by the system.\n\n" "Reason: {reason}\n\n" "If you still need this approval, please submit it again.\n\n" "{cta_line}\n\n" "— Helix IT Platform" ), "body_text_ja": ( "{submitted_at} に提出された承認申請は、システムにより自動的に " "[b]{new_status}[/b] となりました。\n\n" "理由:{reason}\n\n" "引き続き承認が必要な場合は、お手数ですが再度申請してください。\n\n" "{cta_line}\n\n" "— Helix IT プラットフォーム" ), "card_button_label": "查看我的申请", "card_button_label_en": "View my requests", "card_button_label_ja": "申請一覧を見る", }, # ── 月结关账 ───────────────────────────────────────────────────── "finance.close_reminder": { "subject": "月结关账任务提醒 · {label}", "subject_en": "Month-end close reminder · {label}", "subject_ja": "月次決算タスクのご案内 · {label}", "body_text": ( "你有 [b]{overdue}[/b] 项逾期、{soon} 项即将到期的关账任务" "(最近截止 {earliest})。\n\n" "{cta_line}\n\n" "— Helix IT 平台" ), "body_text_en": ( "Month-end close tasks — overdue: [b]{overdue}[/b], due soon: " "{soon}. The earliest one is due {earliest}.\n\n" "{cta_line}\n\n" "— Helix IT Platform" ), "body_text_ja": ( "期限超過 [b]{overdue}[/b] 件、期限間近 {soon} 件の決算タスクが" "あります(直近の期限は {earliest})。\n\n" "{cta_line}\n\n" "— Helix IT プラットフォーム" ), "card_button_label": "查看关账看板", "card_button_label_en": "Open the close board", "card_button_label_ja": "決算ボードを開く", }, # ── 采购提醒(三类) ───────────────────────────────────────────── "procurement.reminder.contract_expiry": { "subject": "合同即将到期:{contract_number}", "subject_en": "Contract expiring soon: {contract_number}", "subject_ja": "契約の有効期限が近づいています:{contract_number}", "body_text": ( "{contract_title}(供应商 ID {supplier_id})将在 " "[b]{days_until}[/b] 天后(于 {expiry_date})到期。" "如需续约请尽快与采购联系。\n\n" "{cta_line}\n\n" "— Helix IT 平台" ), "body_text_en": ( "{contract_title} (supplier ID {supplier_id}) expires on " "{expiry_date} — days remaining: [b]{days_until}[/b]. Please " "contact Procurement soon if it needs to be renewed.\n\n" "{cta_line}\n\n" "— Helix IT Platform" ), "body_text_ja": ( "{contract_title}(ベンダー ID {supplier_id})は " "[b]{days_until}[/b] 日後({expiry_date})に有効期限を迎えます。" "更新が必要な場合は、早めに調達担当までご連絡ください。\n\n" "{cta_line}\n\n" "— Helix IT プラットフォーム" ), "card_button_label": "查看合同", "card_button_label_en": "View contract", "card_button_label_ja": "契約を確認する", }, "procurement.reminder.budget_threshold": { "subject": "预算 {threshold}% 阈值预警:{department} {year}", "subject_en": "Budget {threshold}% threshold alert: {department} {year}", "subject_ja": "予算 {threshold}% 到達のお知らせ:{department} {year}", "body_text": ( "{year} 年度 {department} 部门 {currency} 预算池已占用 " "[b]{percent}%[/b]({locked} / {total})。" "如需扩容请联系财务 / IT 管理员。\n\n" "{cta_line}\n\n" "— Helix IT 平台" ), "body_text_en": ( "The {year} {currency} budget pool for {department} is " "[b]{percent}%[/b] committed ({locked} / {total}). Please contact " "Finance or an IT admin if it needs to be topped up.\n\n" "{cta_line}\n\n" "— Helix IT Platform" ), "body_text_ja": ( "{year} 年度 {department} 部門の {currency} 予算枠は " "[b]{percent}%[/b]({locked} / {total})まで消化されています。" "増枠が必要な場合は、財務または IT 管理者までご連絡ください。\n\n" "{cta_line}\n\n" "— Helix IT プラットフォーム" ), "card_button_label": "查看预算", "card_button_label_en": "View budget", "card_button_label_ja": "予算を確認する", }, "procurement.reminder.order_late": { "subject": "PO {order_number} 在汇联易侧已 {days} 天", "subject_en": "PO {order_number} pending in Huilianyi — days waiting: {days}", "subject_ja": "PO {order_number} が Huilianyi 側で {days} 日間保留されています", "body_text": ( "PO {order_number} 状态仍是 submitted,external_po_number 未填," "提示汇联易那边可能尚未批准。请采购同事跟进。\n\n" "{cta_line}\n\n" "— Helix IT 平台" ), "body_text_en": ( "PO {order_number} is still in `submitted` status and its " "external_po_number is empty, which suggests Huilianyi has not " "approved it yet. Please ask Procurement to follow up.\n\n" "{cta_line}\n\n" "— Helix IT Platform" ), "body_text_ja": ( "PO {order_number} は依然として submitted のままで、" "external_po_number も未入力です。Huilianyi 側で未承認の可能性が" "あります。調達担当までフォローアップをお願いします。\n\n" "{cta_line}\n\n" "— Helix IT プラットフォーム" ), "card_button_label": "查看采购单", "card_button_label_en": "View purchase order", "card_button_label_ja": "発注を確認する", }, # ── 数据同步自动带入的 manager ─────────────────────────────────── "identity_sync.auto_included_manager": { "subject": "你的 {target_name} 账号已自动创建", "subject_en": "Your {target_name} account has been created automatically", "subject_ja": "{target_name} のアカウントを自動作成しました", "body_text": ( "Hi {manager_name},\n\n" "由于你的直接下属中包含外籍员工,Helix 已经在 " "[b]{target_name}[/b] 为你自动创建了账号,以便系统中能够正确关联" "汇报关系。\n\n" " · 同步目标:{target_name}\n" " · 你的工作邮箱:{work_email}\n\n" "首次登录请通过公司 SSO。如有疑问请联系 IT 团队。\n\n" "— Helix IT 平台" ), "body_text_en": ( "Hi {manager_name},\n\n" "Because one or more of your direct reports is a foreign " "national, Helix has automatically created an account for you in " "[b]{target_name}[/b] so the reporting line resolves correctly " "there.\n\n" " · Sync target: {target_name}\n" " · Your work email: {work_email}\n\n" "Please sign in through company SSO the first time. Contact the " "IT team with any questions.\n\n" "— Helix IT Platform" ), "body_text_ja": ( "{manager_name} さん\n\n" "直属の部下に外国籍の従業員が含まれているため、レポートラインを" "正しく連携できるよう、Helix が [b]{target_name}[/b] 上に" "アカウントを自動作成しました。\n\n" " · 同期先:{target_name}\n" " · 業務用メールアドレス:{work_email}\n\n" "初回ログインは社内 SSO からお願いします。ご不明な点は IT チーム" "までお問い合わせください。\n\n" "— Helix IT プラットフォーム" ), }, # ── Atlassian 治理告警(收件人为 IT 管理员) ──────────────────── "jira.leaver_account_audit": { "subject": ( "[治理] {offender_count} 名离职员工 Atlassian 访问持续反弹" "(自动 revoke ≥ {threshold} 次仍未清干净)" ), "subject_en": ( "[Governance] Leavers who keep regaining Atlassian access: " "{offender_count} (auto-revoked {threshold}+ times)" ), "subject_ja": ( "[ガバナンス] 退職者 {offender_count} 名の Atlassian アクセスが" "再付与され続けています(自動剥奪 {threshold} 回以上)" ), "body_text": ( "过去 {window_days} 天内,{offender_count} 名已离职员工被 Helix 从 " "Atlassian 授权 group 中重复移除 ≥ {threshold} 次,该 group 仍每次" "被重新加回 —— 这是真正的源头反弹(SCIM directory 还没删人 / " "default-group 自动包含 / 管理员手动加回)。Helix 已停止继续自动 " "revoke,需要人工排根因:\n\n" "{sample_block}\n\n" "处理:去 Atlassian Admin UI 检查这些账号被反复加回的 group 来源。\n" "(注:仅列出确实还在某个授权 group 里、删了又被加回的人;Trello / " "默认访问 这类非 group 授予的残留单独告警。)\n\n" "— Helix IT 平台" ), "body_text_en": ( "Departed employees whose Atlassian group membership keeps coming " "back: {offender_count}. Over the last {window_days} days, Helix " "removed each of them from their access group {threshold} or more " "times, and every time the membership returned — a genuine " "upstream bounce (the SCIM directory still holds " "the person, a default group auto-includes them, or an admin " "re-added them by hand). Helix has stopped auto-revoking these; a " "human root-cause pass is needed:\n\n" "{sample_block}\n\n" "Next step: in the Atlassian Admin UI, check where these accounts " "keep being re-added from.\n" "(Note: this lists only people who are still in an access group " "and get re-added after removal. Residual access that no group " "grants — Trello, org default access — is alerted separately.)\n\n" "— Helix IT Platform" ), "body_text_ja": ( "過去 {window_days} 日間に、退職済みの従業員 {offender_count} 名を " "Atlassian の権限グループから {threshold} 回以上削除しましたが、" "そのたびに再度追加されています。上流側での再付与(SCIM " "ディレクトリに在籍のまま / デフォルトグループによる自動追加 / " "管理者による手動追加)が原因と考えられます。Helix は自動剥奪を" "停止しましたので、原因の切り分けをお願いします。\n\n" "{sample_block}\n\n" "対応:Atlassian 管理画面で、これらのアカウントが再追加されている" "経路をご確認ください。\n" "(注:権限グループに残っており、削除後に再追加された方のみを記載" "しています。Trello や組織の既定アクセスなど、グループ以外で付与" "された残存アクセスは別途通知します。)\n\n" "— Helix IT プラットフォーム" ), }, "jira.leaver_account_audit.stale_active_account": { "subject": ( "[治理] {offender_count} 名离职员工的 Atlassian 账号仍未停用" " —— 身份目录同步可能已断" ), "subject_en": ( "[Governance] {offender_count} leaver Atlassian account(s) are " "still active — the identity directory sync may be down" ), "subject_ja": ( "[ガバナンス] 退職者 {offender_count} 名の Atlassian アカウントが" "無効化されていません —— ID ディレクトリ同期が停止している可能性" ), "body_text": ( "{offender_count} 名已离职员工的 Atlassian [b]账号本身[/b]在离职 " "{grace_days} 天后仍是 active(最久的一个已 " "{oldest_days_since_termination} 天):\n\n" "{sample_block}\n\n" "[b]这不是 Helix 的撤权失败[/b] —— Helix 按既定策略只移除 group " "与产品访问,从不删除 Atlassian 账号(保留 issue 历史归属)。真正" "把离职者账号置为 inactive 的是组织侧的[b]身份目录同步[/b]" "(Atlassian Guard 的 Google Workspace / SCIM 用户供给)。它连续" "多天没有生效,说明那条链路断了。\n\n" "排查:admin.atlassian.com → Security → User security → Identity " "providers → 对应目录,确认 [b]Provisioning 是否为 Disabled[/b]、" "Last sync 停在哪一天、是否有同步报错。\n" "⚠️ 该设置页的『保存』会一次性提交整个表单(同步开关、邀请邮件" "文案、登录策略),改任何一项都可能连带改掉同步开关 —— 保存前" "逐项确认。\n\n" "Helix 侧的全貌在 离职管理 → 账号终态。\n\n" "— Helix IT 平台" ), "body_text_en": ( "{offender_count} departed employees still have an ACTIVE " "Atlassian account {grace_days}+ days after their last day " "(the oldest is {oldest_days_since_termination} days):\n\n" "{sample_block}\n\n" "[b]This is not a Helix revoke failure[/b]. By policy Helix " "removes groups and product access and never deletes the " "Atlassian account, so issue history stays attributed to a real " "person. What actually flips a leaver to inactive is your org's " "[b]identity directory sync[/b] (Atlassian Guard's Google " "Workspace / SCIM user provisioning). Days without it taking " "effect means that chain is down.\n\n" "Check: admin.atlassian.com → Security → User security → Identity " "providers → your directory. Is [b]Provisioning Disabled[/b]? " "What date does Last sync stop at? Any sync error?\n" "⚠️ Save on that page submits the WHOLE form — the sync toggle, " "the new-user invite message and the login policy together. " "Editing any one of them can switch the sync off with it; " "re-check every field before saving.\n\n" "The Helix-side picture is under Offboarding → Account end " "state.\n\n" "— Helix IT Platform" ), "body_text_ja": ( "退職者 {offender_count} 名の Atlassian [b]アカウント自体[/b]が、" "退職から {grace_days} 日を過ぎても active のままです" "(最長で {oldest_days_since_termination} 日):\n\n" "{sample_block}\n\n" "[b]これは Helix の権限剥奪の失敗ではありません[/b] —— Helix は" "方針としてグループと製品アクセスのみを削除し、Atlassian " "アカウントは削除しません(課題履歴の帰属を保つため)。退職者を " "inactive にするのは組織側の[b]ID ディレクトリ同期[/b]" "(Atlassian Guard の Google Workspace / SCIM ユーザープロビジョ" "ニング)です。数日にわたり反映されていない場合、その経路が" "止まっています。\n\n" "確認:admin.atlassian.com → Security → User security → Identity " "providers → 対象ディレクトリ。[b]Provisioning が Disabled[/b] に" "なっていないか、Last sync がいつで止まっているか、同期エラーが" "ないか。\n" "⚠️ この設定ページの「保存」はフォーム全体(同期スイッチ・招待" "メール文面・ログインポリシー)を一度に送信します。いずれか一つを" "変更しただけで同期スイッチまで変わり得るため、保存前に各項目を" "確認してください。\n\n" "Helix 側の全体像は 退職管理 → アカウント最終状態 にあります。\n\n" "— Helix IT プラットフォーム" ), }, "identity_chain.idp_sync_stale": { "subject": ( "[治理] Atlassian 身份目录同步已停止 —— 离职账号不再被自动停用" ), "subject_en": ( "[Governance] Atlassian identity directory sync has stopped — " "leaver accounts are no longer deactivated" ), "subject_ja": ( "[ガバナンス] Atlassian の ID ディレクトリ同期が停止 —— 退職者" "アカウントが自動で無効化されません" ), "body_text": ( "Atlassian 侧的 [b]Google Workspace / SCIM 用户供给[/b]已经" "停止:\n\n" " · 最后一次成功同步:{last_beat_at}\n" " · 最近一次被关闭:{disabled_at}(操作人 {disabled_by})\n\n" "[b]为什么这条要紧[/b]:把离职者的 Atlassian 账号置为 inactive 的" "一直是这条同步,不是 Helix —— Helix 按既定策略只撤 group 与产品" "访问、从不删账号。这条链路停着的每一天,离职的人都保留着可登录的" "账号,新入职的人则一个账号都建不出来。\n\n" "[b]怎么修[/b]:admin.atlassian.com → Security → User security → " "Identity providers → 对应目录,确认 Provisioning 不是 Disabled;" "页面上的 Last sync 会告诉你停在哪一天。\n" "⚠️ 那个设置页的『保存』会一次性提交整个表单(同步开关 / 新人" "邀请邮件文案 / 登录策略)—— 2026-08-13 那次就是去改邀请邮件文案、" "保存时把同步开关一起关掉的,两条审计事件相隔 74 毫秒。改任何一项" "之前,先看清同步开关处在什么状态。\n\n" "Helix 侧的全貌在 离职管理 → 账号终态。\n\n" "— Helix IT 平台" ), "body_text_en": ( "Atlassian's [b]Google Workspace / SCIM user provisioning[/b] has " "stopped:\n\n" " · Last successful sync: {last_beat_at}\n" " · Last switched off: {disabled_at} by {disabled_by}\n\n" "[b]Why this matters[/b]: what flips a leaver's Atlassian account " "to inactive has always been this sync, not Helix — by policy " "Helix only revokes groups and product access and never deletes " "the account. Every day this chain is down, leavers keep " "sign-in-capable accounts and new hires get none at all.\n\n" "[b]How to fix[/b]: admin.atlassian.com → Security → User " "security → Identity providers → your directory. Confirm " "Provisioning is not Disabled; the page's Last sync tells you " "what date it stopped.\n" "⚠️ Save on that page submits the WHOLE form — sync toggle, " "new-user invite message, login policy. On 2026-08-13 an edit to " "the invite message switched the sync off with it; the two audit " "events are 74 ms apart. Check the sync toggle's state before " "changing anything else.\n\n" "The Helix-side picture is under Offboarding → Account end " "state.\n\n" "— Helix IT Platform" ), "body_text_ja": ( "Atlassian 側の [b]Google Workspace / SCIM ユーザープロビジョ" "ニング[/b]が停止しています:\n\n" " · 最後に成功した同期:{last_beat_at}\n" " · 直近の無効化:{disabled_at}(実行者 {disabled_by})\n\n" "[b]なぜ重要か[/b]:退職者の Atlassian アカウントを inactive に" "してきたのは常にこの同期であり、Helix ではありません —— Helix は" "方針としてグループと製品アクセスのみを剥奪し、アカウントを削除" "しません。この経路が止まっている間、退職者はログイン可能な" "アカウントを保持し、新入社員にはアカウントが作られません。\n\n" "[b]対処[/b]:admin.atlassian.com → Security → User security → " "Identity providers → 対象ディレクトリ。Provisioning が Disabled " "でないことを確認してください。ページの Last sync がいつ止まった" "かを示します。\n" "⚠️ この設定ページの「保存」はフォーム全体(同期スイッチ・新規" "ユーザー招待メール文面・ログインポリシー)を一度に送信します。" "2026-08-13 は招待メール文面の変更で同期まで無効化されました" "(監査イベントの差は 74 ミリ秒)。他の項目を変更する前に、同期" "スイッチの状態を確認してください。\n\n" "Helix 側の全体像は 退職管理 → アカウント最終状態 にあります。\n\n" "— Helix IT プラットフォーム" ), }, "jira.leaver_account_audit.non_group_residual": { "subject": ( "[治理] {offender_count} 名离职员工 Atlassian 残留访问非 group 授予" "(需人工处理,Helix 无法自动清)" ), "subject_en": ( "[Governance] Leavers holding Atlassian access that no group " "grants: {offender_count} (manual cleanup required)" ), "subject_ja": ( "[ガバナンス] 退職者 {offender_count} 名にグループ由来ではない " "Atlassian アクセスが残っています(手動対応が必要です)" ), "body_text": ( "{offender_count} 名已离职员工在 Atlassian 上仍报有产品访问,且" "这些访问[b]不由任何 group 授予[/b](Trello / 组织默认访问 / " "直接授权)。Helix 未能自动清除以下这些,需要人工跟进" "(每行附原因):\n\n" "{sample_block}\n\n" "最常见原因:[b]未配置 org_unscoped_api_key[/b](此时 Helix 不会" "尝试调用 API)—— 撤访问的端点不在 granular scope 体系里,带 " "scope 的 key 会 401,必须在 admin.atlassian.com/o//" "api-keys 新建一把[b]不选任何 scope[/b] 的 org API key,填进 Jira " "integration 的 org_unscoped_api_key。配好后下个对账周期会自动撤" "干净、不再打扰。\n" "若 key 已配但 API 仍拒绝(403 / 401),或 Trello 工作区成员制清" "不掉:请在 trello.com / Atlassian Admin UI 里手动移出工作区或" "停用账号。\n\n" "— Helix IT 平台" ), "body_text_en": ( "Departed employees who still report product access in Atlassian: " "{offender_count}. [b]No group grants that access[/b] " "(Trello, org default access, or a direct grant). Helix could not " "clear the following automatically; each line carries its " "reason:\n\n" "{sample_block}\n\n" "Most common cause: [b]org_unscoped_api_key is not " "configured[/b], in which case Helix does not even attempt the " "call. The remove-access endpoint is outside the granular-scope " "system, so a scoped key returns 401. Create an org API key with " "[b]no scopes selected[/b] at admin.atlassian.com/o//" "api-keys and save it as org_unscoped_api_key on the Jira " "integration. Once configured, the next reconciliation cycle " "cleans these up and this alert stops.\n" "If the key is configured but the API still refuses (403 / 401), " "or Trello workspace membership cannot be cleared: remove the " "account from the workspace or deactivate it manually via " "trello.com or the Atlassian Admin UI.\n\n" "— Helix IT Platform" ), "body_text_ja": ( "退職済みの従業員 {offender_count} 名に、Atlassian 上で製品への" "アクセスが残っています。これらは[b]いずれのグループからも付与" "されていません[/b](Trello / 組織の既定アクセス / 直接付与)。" "以下は Helix が自動的に解除できなかったものです。各行に理由を" "記載していますので、手動での対応をお願いします。\n\n" "{sample_block}\n\n" "最も多い原因は [b]org_unscoped_api_key が未設定[/b]であることです" "(この場合 Helix は API を呼び出しません)。アクセス解除の " "API は granular scope の対象外のため、スコープ付きの key では " "401 になります。admin.atlassian.com/o//api-keys で" "[b]スコープを一つも選択しない[/b] org API key を新規作成し、" "Jira インテグレーションの org_unscoped_api_key に設定してくださ" "い。設定後は次回の棚卸しサイクルで自動的に解除され、この通知も" "止まります。\n" "key を設定しても API が拒否する場合(403 / 401)、または Trello " "のワークスペース メンバーシップを解除できない場合は、trello.com " "もしくは Atlassian 管理画面から手動でワークスペースから削除する" "か、アカウントを無効化してください。\n\n" "— Helix IT プラットフォーム" ), }, "jira.recently_revoked_audit": { "subject": ( "[治理] {offender_count} 名员工 Atlassian 撤销持续反弹" "(自动重撤 ≥ {threshold} 次仍未清干净)" ), "subject_en": ( "[Governance] Employees whose Atlassian revocation keeps bouncing " "back: {offender_count} (auto-revoked {threshold}+ times)" ), "subject_ja": ( "[ガバナンス] 従業員 {offender_count} 名の Atlassian 権限剥奪が" "戻され続けています(自動再剥奪 {threshold} 回以上)" ), "body_text": ( "{offender_count} 名在职员工在 admin-revoke / 自助取消之后," "Atlassian product_access 被 Helix 自动重撤 ≥ {threshold} 次仍" "持续反弹(SCIM / default-group / 误操作把人在源头反复加回)。" "Helix 已停止对这些(员工, 产品)继续自动 revoke,需要人工" "排根因:\n\n" "{sample_block}\n\n" "处理:去 Atlassian Admin UI 复查 group 来源,并联系 SCIM / IdP " "同步源(Okta / Google IdP / 钉钉 SCIM)定位反弹源头。\n\n" "— Helix IT 平台" ), "body_text_en": ( "Current employees whose Atlassian product_access came back after " "an admin revoke or a self-service cancellation: " "{offender_count}. It kept coming back even after Helix " "auto-revoked it {threshold} or more times (SCIM, a default " "group, or a mistaken re-add keeps restoring it upstream). Helix " "has stopped auto-revoking these (employee, product) pairs; a " "human root-cause pass is needed:\n\n" "{sample_block}\n\n" "Next step: review the group sources in the Atlassian Admin UI, " "then contact the owners of the SCIM / IdP sync (Okta, Google " "IdP, DingTalk SCIM) to locate where the re-add comes from.\n\n" "— Helix IT Platform" ), "body_text_ja": ( "在職中の従業員 {offender_count} 名について、管理者による剥奪" "またはセルフサービスでの解約のあとも Atlassian の " "product_access が復活しており、Helix が {threshold} 回以上" "自動で再剥奪しても戻され続けています(SCIM / デフォルト" "グループ / 誤操作により、上流で繰り返し再付与されているものと" "考えられます)。Helix は該当する(従業員, 製品)の自動剥奪を" "停止しましたので、原因の切り分けをお願いします。\n\n" "{sample_block}\n\n" "対応:Atlassian 管理画面でグループの由来を確認のうえ、SCIM / " "IdP 連携元(Okta / Google IdP / DingTalk SCIM)の担当者に" "再付与の発生源をご確認ください。\n\n" "— Helix IT プラットフォーム" ), }, # ── 数据同步治理告警(收件人 = 该目标的 alert_recipient_ids) ───── # 明细行不进正文:这四条告警把编码 / 邮箱 / 原因放在 ``card_sections`` # 里,正文只承载散文 —— 与上线时的原文一致,也避免飞书卡片把同一份 # 清单渲染两遍。 "identity_sync.unmapped_depts": { "subject": "🗂️ 数据同步:{unmapped_count} 个部门缺少映射 — {target_name}", "subject_en": ( "🗂️ Identity sync — departments with no mapping: " "{unmapped_count} ({target_name})" ), "subject_ja": ( "🗂️ データ同期:マッピング未設定の部門 {unmapped_count} 件 — " "{target_name}" ), "body_text": ( "下列部门在 Helix 有组织节点,但汇联易映射表里没有对应的老部门" "编码。同步会用原始编码推送,可能在汇联易新建一个与 NS/老系统" "不一致的平行部门,导致成本归集错乱。\n\n" "请为每个部门补一行映射(Helix 编码 → 汇联易老编码);若确属全新" "部门,映射到其自身编码以显式放行。\n\n" "{cta_line}\n\n" "— Helix IT 平台" ), "body_text_en": ( "The departments listed below have an org node in Helix but no " "legacy department code in the Huilianyi mapping table. The sync " "pushes them under their raw code, which can create a parallel " "department in Huilianyi that does not match the NS / legacy one " "and scrambles cost attribution.\n\n" "Please add one mapping row per department (Helix code → legacy " "Huilianyi code). If a department really is new, map it to its " "own code to let it through explicitly.\n\n" "{cta_line}\n\n" "— Helix IT Platform" ), "body_text_ja": ( "以下の部門は Helix には組織ノードがありますが、Huilianyi の" "マッピング表に対応する旧部門コードがありません。同期は元の" "コードのまま連携するため、NS / 旧システムと一致しない並行部門が " "Huilianyi 側に作成され、コスト集計が崩れるおそれがあります。\n\n" "各部門について、マッピングを 1 行ずつ追加してください" "(Helix コード → Huilianyi 旧コード)。新設部門であることが" "確かな場合は、自身のコードにマッピングして明示的に許可して" "ください。\n\n" "{cta_line}\n\n" "— Helix IT プラットフォーム" ), "card_button_label": "查看同步目标", "card_button_label_en": "Open the sync target", "card_button_label_ja": "同期ターゲットを開く", }, "identity_sync.value_map_misses": { "subject": "🔤 数据同步:{miss_count} 个字段值缺少映射 — {target_name}", "subject_en": ( "🔤 Identity sync — field values with no mapping: " "{miss_count} ({target_name})" ), "subject_ja": ( "🔤 データ同期:マッピング未設定の項目値 {miss_count} 件 — " "{target_name}" ), "body_text": ( "下列「字段=源值」在同步目标的 value_map 里没有对应条目。" "STRICT 映射会把未命中的值渲染为空 → 该字段被丢弃、下游对应属性" "被冻结(且运行仍显示绿色)。\n\n" "请为每个新值补一行映射;若确属预期,映射到其自身以显式放行。" "补齐后相关人员会自动重新同步。\n\n" "{cta_line}\n\n" "— Helix IT 平台" ), "body_text_en": ( "The `field=source value` pairs listed below have no entry in " "this target's value_map. A STRICT mapping renders an unmatched " "value as empty, so the field is dropped and the downstream " "attribute freezes — while the run still reports green.\n\n" "Please add one mapping row per new value. If the value is " "expected, map it to itself to let it through explicitly. Once " "the map is complete, the affected people re-sync " "automatically.\n\n" "{cta_line}\n\n" "— Helix IT Platform" ), "body_text_ja": ( "以下の「項目=ソース値」は、この同期ターゲットの value_map に" "対応する定義がありません。STRICT マッピングでは未一致の値は空と" "して扱われるため、その項目は連携されず、下流の属性が更新されない" "まま固定されます(実行結果は正常のままです)。\n\n" "新しい値ごとにマッピングを 1 行ずつ追加してください。想定内の値" "であれば、自身の値にマッピングして明示的に許可してください。" "補完後、該当者は次回以降の同期で自動的に再連携されます。\n\n" "{cta_line}\n\n" "— Helix IT プラットフォーム" ), "card_button_label": "查看同步目标", "card_button_label_en": "Open the sync target", "card_button_label_ja": "同期ターゲットを開く", }, # ``{create_missing_note_*}`` 是 locale-fragment 三元组:开没开 # ``netsuite_create_missing`` 决定了这封信是「预警」还是「现在就会建平行 # 部门」,两种措辞完全不同,由调用点按当前配置选一组塞进 context。 "identity_sync.unadopted_ns_depts": { "subject": "🗂️ NetSuite:{unadopted_count} 个部门未采纳 — {target_name}", "subject_en": ( "🗂️ NetSuite — departments not yet adopted: " "{unadopted_count} ({target_name})" ), "subject_ja": ( "🗂️ NetSuite:未取り込みの部門 {unadopted_count} 件 — {target_name}" ), "body_text": ( "{create_missing_note_zh}\n\n" "{cta_line}\n\n" "— Helix IT 平台" ), "body_text_en": ( "{create_missing_note_en}\n\n" "{cta_line}\n\n" "— Helix IT Platform" ), "body_text_ja": ( "{create_missing_note_ja}\n\n" "{cta_line}\n\n" "— Helix IT プラットフォーム" ), "card_button_label": "查看同步目标", "card_button_label_en": "Open the sync target", "card_button_label_ja": "同期ターゲットを開く", }, "identity_sync.blocked_backlog": { "subject": "🚧 数据同步:{blocked_count} 人被下游拒绝待处理 — {target_name}", "subject_en": ( "🚧 Identity sync — people rejected downstream and awaiting " "action: {blocked_count} ({target_name})" ), "subject_ja": ( "🚧 データ同期:連携先に拒否され対応待ちの従業員 {blocked_count} 名" " — {target_name}" ), "body_text": ( "下列人员的同步被下游系统以「非临时性」原因拒绝 —— 属于数据或配置" "问题,重试不会自愈,所以运行状态仍是绿色、也不会反复重试。\n\n" "请按每行的原因处理(补数据 / 改配置 / 在下游腾出容量);处理完这些" "人会在下一次同步自动重新推送。\n\n" "{cta_line}\n\n" "— Helix IT 平台" ), "body_text_en": ( "The people listed below were rejected by the downstream system " "for a non-transient reason — a data or configuration problem " "that a retry cannot heal, which is why the run still reports " "green and nothing is retried.\n\n" "Please act on the reason given for each line (fill in the data, " "fix the configuration, or free up capacity downstream). Once " "that is done, these people are pushed again automatically on the " "next sync.\n\n" "{cta_line}\n\n" "— Helix IT Platform" ), "body_text_ja": ( "以下の従業員の同期は、連携先システムにより「一時的ではない」理由" "で拒否されました。データまたは設定の問題であり、再試行では解消" "しないため、実行結果は正常のままで再試行も行われません。\n\n" "各行に記載された理由に沿って対応してください(データの補完 / " "設定の修正 / 連携先の空き枠の確保)。対応が完了すると、次回の同期" "で自動的に再連携されます。\n\n" "{cta_line}\n\n" "— Helix IT プラットフォーム" ), "card_button_label": "查看同步目标", "card_button_label_en": "Open the sync target", "card_button_label_ja": "同期ターゲットを開く", }, # ── 飞书撤回对账(bell-only,收件人为 global_admin) ─────────────── # ``{error_code_label_*}`` / ``{remediation_*}`` 是 locale-fragment 三元 # 组:错误码可能取不到(「未知」),而 60009 有一条精确的后台修复步骤, # 与通用建议是两段不同的话 —— 由调用点按实际错误码选一组。 "approval_feishu_cancel_desync": { "subject": "飞书撤回同步失败:{flow_label}", "subject_en": "Feishu withdrawal did not sync: {flow_label}", "subject_ja": "Feishu への取り消し同期に失敗しました:{flow_label}", "body_text": ( "用户在 Helix 撤回了一条「{flow_label}」(ID: {resource_id})," "本地状态已置为已撤回,但同步撤回飞书审批单失败。\n\n" "飞书实例:{instance_code}\n" "飞书错误码:{error_code_label_zh}\n" "错误详情:{error_detail}\n\n" "影响:飞书侧审批单可能仍显示「审批中」,审批人会继续收到待办;" "若审批人随后在飞书通过该单,回调会因本地已撤回而被忽略。\n\n" "{remediation_zh}\n\n" "— Helix IT 平台" ), "body_text_en": ( "A user withdrew a '{flow_label}' request in Helix " "(ID: {resource_id}). The Helix record is marked withdrawn, but " "withdrawing the matching Feishu approval instance failed.\n\n" "Feishu instance: {instance_code}\n" "Feishu error code: {error_code_label_en}\n" "Error detail: {error_detail}\n\n" "Impact: the approval may still show as in review on the Feishu " "side and approvers keep seeing it in their to-do list. If an " "approver approves it there later, the callback is ignored " "because Helix has already withdrawn the request.\n\n" "{remediation_en}\n\n" "— Helix IT Platform" ), "body_text_ja": ( "ユーザーが Helix で「{flow_label}」の申請を取り消しました" "(ID: {resource_id})。Helix 側は取り消し済みになりましたが、" "対応する Feishu の承認申請の取り消しに失敗しました。\n\n" "Feishu インスタンス:{instance_code}\n" "Feishu エラーコード:{error_code_label_ja}\n" "エラー詳細:{error_detail}\n\n" "影響:Feishu 側では承認申請が「審査中」のまま表示され、承認者の" "未処理一覧に残り続ける可能性があります。その後に承認者が Feishu " "上で承認しても、Helix 側は取り消し済みのためコールバックは無視" "されます。\n\n" "{remediation_ja}\n\n" "— Helix IT プラットフォーム" ), }, # ── 半年度访问自查 ────────────────────────────────────────────── # 三个 key:开跑通知 / 逾期催办(持有人)/ 逾期知会(其 manager)。三句 # 完全不同的话,而一个 template_key 只解析出一份文案 —— 三者原本共用 # ``access_review.self_attestation``,只因为它没注册、每个人都拿到调用点 # 那份中文,才没暴露出来。 "access_review.self_attestation": { "subject": "半年度访问自查:请确认你的访问权限", "subject_en": "Semi-annual access review: please confirm your access", "subject_ja": ( "半年ごとのアクセス点検:アクセス権のご確認をお願いします" ), "body_text": ( "按公司安全合规要求,我们每半年请你确认一次名下的访问权限。\n\n" "你当前持有 [b]{total}[/b] 项访问(其中 {elevated} 项为高敏感:" "云授权 / API Key / SSO)。请在 [r]{due_label}[/r] 前完成确认 —— " "还需要的勾选确认,不再需要的可一键标记清理。\n\n" "全程一屏批量完成,无需逐项处理。\n\n" "{cta_line}\n\n" "— Helix IT 平台" ), "body_text_en": ( "Company security and compliance ask you to confirm the access " "you hold once every six months.\n\n" "Access you hold right now: [b]{total}[/b], of which {elevated} " "are highly sensitive (cloud grants, API keys, SSO). Please " "confirm before [r]{due_label}[/r] — tick what you still need, " "and flag the rest for clean-up in one click.\n\n" "It is one screen, done in bulk — there is nothing to work " "through item by item.\n\n" "{cta_line}\n\n" "— Helix IT Platform" ), "body_text_ja": ( "社内のセキュリティおよびコンプライアンス要件により、半年ごとに" "保有するアクセス権のご確認をお願いしています。\n\n" "現在保有しているアクセス:[b]{total}[/b] 件(うち機微なもの " "{elevated} 件 — クラウド権限 / API キー / SSO)。" "[r]{due_label}[/r] までにご確認ください。引き続き必要なものは" "チェックし、不要なものはワンクリックで整理対象にできます。\n\n" "1 画面でまとめて完了でき、1 件ずつ処理する必要はありません。\n\n" "{cta_line}\n\n" "— Helix IT プラットフォーム" ), "card_button_label": "去确认", "card_button_label_en": "Review now", "card_button_label_ja": "確認する", }, "access_review.self_attestation.overdue": { "subject": "访问自查已逾期,请尽快完成", "subject_en": "Your access self-attestation is overdue", "subject_ja": "アクセス自己点検の期限が過ぎています", "body_text": ( "你还有 [b]{pending_count}[/b] 项访问权限未在半年度自查中确认," "已超过截止日期。\n\n" "请尽快完成 —— 还需要的确认保留,不再需要的一键清理。\n\n" "{cta_line}\n\n" "— Helix IT 平台" ), "body_text_en": ( "You have access items you have not confirmed in the semi-annual " "self-attestation, and the deadline has passed. Items still " "waiting on you: [b]{pending_count}[/b].\n\n" "Please finish it soon — keep what you still need, and clear what " "you don't in one click.\n\n" "{cta_line}\n\n" "— Helix IT Platform" ), "body_text_ja": ( "半年ごとのアクセス自己点検で未確認のアクセス権が残っており、" "期限を過ぎています。未確認の件数:[b]{pending_count}[/b]。\n\n" "引き続き必要なものは保持を、不要なものはワンクリックで削除を" "選択し、お早めに完了してください。\n\n" "{cta_line}\n\n" "— Helix IT プラットフォーム" ), "card_button_label": "去确认", "card_button_label_en": "Review now", "card_button_label_ja": "確認する", }, "access_review.self_attestation.overdue_manager": { "subject": "下属访问自查逾期", "subject_en": "A direct report's access self-attestation is overdue", "subject_ja": "部下のアクセス自己点検が期限を過ぎています", "body_text": ( "你的下属 [b]{holder_name}[/b] 有 {pending_count} 项访问权限未在" "半年度自查中确认,已逾期。\n\n" "请提醒其尽快完成。\n\n" "— Helix IT 平台" ), "body_text_en": ( "Your direct report [b]{holder_name}[/b] has not finished the " "semi-annual access self-attestation and is now past the " "deadline. Items still waiting on them: {pending_count}.\n\n" "Please remind them to complete it.\n\n" "— Helix IT Platform" ), "body_text_ja": ( "部下の [b]{holder_name}[/b] さんが、半年ごとのアクセス自己点検を" "完了しないまま期限を過ぎています。未確認の件数:" "{pending_count}。\n\n" "お早めに完了するようお伝えください。\n\n" "— Helix IT プラットフォーム" ), }, # ``access_certification_service::_open_no_response_followup`` —— 自证收尾时 # 一个下属根本没回答,决定权因此落到经理头上。 # # 这条模板存在的理由是「记下谁该处理 ≠ 派给他」:补审 campaign 把 # ``assigned_reviewer_person_id`` 设成经理就算「派了」,但经理不会去刷审计 # 控制台(那是 auditor 的门),也不一定用 MCP 客户端。没有这条推送 + 我的待办 # 里那个页签,整个「后果」就只是数据库里的一行,谁也不会看见。 "access_review.no_response_followup": { "subject": "需要你替下属确认访问权限", "subject_en": "A direct report's access needs your call", "subject_ja": "部下のアクセス権限の確認をお願いします", "body_text": ( "半年度访问自查已经结束,你的下属 [b]{holder_name}[/b] 有 " "{item_count} 项权限**没有回答**。\n\n" "这些权限[b]没有被自动撤销[/b] —— 现在需要你替他判断:还需要的留着," "不需要的撤掉。\n\n" "在「我的待办 ▸ 替下属复核」里处理:{link}\n\n" "— Helix IT 平台" ), "body_text_en": ( "The semi-annual access self-attestation has closed, and your " "direct report [b]{holder_name}[/b] left {item_count} item(s) " "unanswered.\n\n" "Nothing was revoked automatically — the call is now yours: keep " "what they still need, remove what they do not.\n\n" "Handle it under My tasks / Review for a report: {link}\n\n" "— Helix IT Platform" ), "body_text_ja": ( "半年ごとのアクセス自己点検が終了しましたが、部下の " "[b]{holder_name}[/b] さんは {item_count} 件を未回答のままです。\n\n" "権限は自動的に取り消されていません。必要なものは残し、不要なものは" "削除する判断をお願いします。\n\n" "「マイタスク ▸ 部下の代理レビュー」から対応してください:{link}\n\n" "— Helix IT プラットフォーム" ), }, # ``app/intelligence/recommendations.py::send_recommendation_notifications`` # —— AI 分析产出的一条治理建议,推给全体 IT 管理员。 # # 与本文件其它模板不同的是:这里的 subject / body 主体是**每条建议自己的 # 文案**,不是固定散文 —— 所以模板只提供前缀 + 尾巴 + 严重级别那一行, # 建议正文通过 locale fragment 从 context 取。模型只产出中英两版 # (``title_zh`` / ``title_en``),日语位由调用点用英文填,与 # ``FALLBACK_CHAIN`` 的 ja → en 退化方向一致:日语读者看到的是日语外壳 + # 英文正文,而不是中文。 # # ``severity`` 是后端枚举(critical / high / medium / low),三语都直接显示 # 原值 —— 它同时是筛选条件和口头用语,翻译反而对不上管理台里的筛选项。 # context: rec_title_zh/_en/_ja / rec_summary_zh/_en/_ja / severity。 "intelligence.recommendation": { "subject": "[AI 建议] {rec_title_zh}", "subject_en": "[AI recommendation] {rec_title_en}", "subject_ja": "[AI 提案] {rec_title_ja}", "body_text": ( "{rec_summary_zh}\n\n" "严重级别:[b]{severity}[/b]\n\n" "这条建议由 Helix 的分析任务产出。打开后请「开始处理」进入对应工作台," "或填写原因「忽略」;处理完成后再标记为已解决 —— 未处理的新建议" "无法直接标记已解决。已解决 / 已忽略都会计入建议采纳率。\n\n" "{cta_line}\n\n— Helix IT 平台" ), "body_text_en": ( "{rec_summary_en}\n\n" "Severity: [b]{severity}[/b]\n\n" "Helix's analysis job produced this recommendation. Open it and " "either start handling it — which hands you off to the matching " "workbench — or ignore it with a reason. Mark it resolved once the " "handling is done; an untouched new recommendation cannot be " "resolved directly. Resolved and ignored both feed the " "adoption-rate metric.\n\n" "{cta_line}\n\n— Helix IT Platform" ), "body_text_ja": ( "{rec_summary_ja}\n\n" "深刻度:[b]{severity}[/b]\n\n" "この提案は Helix の分析ジョブが生成したものです。開いて「対応を開始」" "して該当のワークベンチへ進むか、理由を添えて「無視」してください。" "「対応済み」は対応が完了してから記録します —— 未着手の新規提案を" "そのまま「対応済み」にすることはできません。「対応済み」「無視」は" "どちらも採用率の集計対象になります。\n\n" "{cta_line}\n\n— Helix IT プラットフォーム" ), "card_button_label": "查看建议", "card_button_label_en": "Open recommendation", "card_button_label_ja": "提案を開く", }, # Deterministic snapshot signal only: it is deliberately separate from # model recommendation copy so an LLM's narrative/severity cannot page an # administrator. context: critical_count / critical_domains_zh/_en/_ja. "intelligence.critical_health": { "subject": "[AI 大脑] {critical_count} 个高风险领域需要复核", "subject_en": "[Intelligence Center] {critical_count} high-risk domain(s) need review", "subject_ja": "[AI 大脳] 高リスク領域 {critical_count} 件の確認が必要です", "body_text": ( "最新确定性健康快照发现高风险领域:{critical_domains_zh}。\n\n" "该提醒只由固定评分阈值和足够的数据可信度触发,不代表 AI 自动下结论。" "请在 AI 大脑查看扣分依据、处理方案和复验状态。\n\n{cta_line}\n\n— Helix IT 平台" ), "body_text_en": ( "The latest deterministic health snapshot found high-risk domains: {critical_domains_en}.\n\n" "This alert is triggered only by fixed scoring thresholds with sufficient data confidence; it is not an AI conclusion. " "Review the evidence, handling plan, and recheck state in Intelligence Center.\n\n{cta_line}\n\n— Helix IT Platform" ), "body_text_ja": ( "最新の決定論的ヘルススナップショットで高リスク領域を検出しました:{critical_domains_ja}。\n\n" "この通知は固定の採点閾値と十分なデータ信頼度のみで発火し、AI の自動結論ではありません。" "AI 大脳で根拠、対応計画、再確認状態を確認してください。\n\n{cta_line}\n\n— Helix IT プラットフォーム" ), "card_button_label": "打开 AI 大脑", "card_button_label_en": "Open Intelligence Center", "card_button_label_ja": "AI 大脳を開く", }, # ── 身份数据质量:新增高危问题(发给 IT global_admin) ────────── # context: count, sample_lines (预拼的项目符号块;``samples`` 是 list, # 直接插值会渲染成 Python repr) "identity.data_quality_new_high": { "subject": "[身份] {count} 个新的高危身份数据质量问题", "subject_en": "[Identity] {count} new high-risk data-quality findings", "subject_ja": "[ID] 重大なデータ品質の問題が {count} 件", "body_text": ( "身份数据质量扫描发现 {count} 个新的高危问题" "(离职仍可登录 / HRIS 已清除仍可登录)。\n\n" "{sample_lines}\n\n" "请到「身份数据质量」面板处理。\n\n" "{cta_line}\n\n" "— Helix IT 平台" ), "body_text_en": ( "The identity data-quality scan found {count} new high-risk " "findings (can still sign in after leaving / can still sign in " "after HRIS removal).\n\n" "{sample_lines}\n\n" "Please work through them on the Identity data-quality panel.\n\n" "{cta_line}\n\n" "— Helix IT Platform" ), "body_text_ja": ( "ID データ品質スキャンで、重大な問題が新たに {count} 件見つかりました" "(退職後もログイン可能 / HRIS 削除後もログイン可能)。\n\n" "{sample_lines}\n\n" "「ID データ品質」パネルからご対応ください。\n\n" "{cta_line}\n\n" "— Helix IT プラットフォーム" ), "card_button_label": "去处理", "card_button_label_en": "Open the panel", "card_button_label_ja": "パネルを開く", }, } __all__ = ["TEMPLATES"]