Skip to content

docs(service): 把升级触发条件、Critical 违约行与三处技能能力主语按源码写实 (#886, #890) - #907

Merged
yinlianghui merged 1 commit into
mainfrom
claude/issue-886-890-service-behavior-residue
Aug 6, 2026
Merged

yinlianghui merged 1 commit into
mainfrom
claude/issue-886-890-service-behavior-residue

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes #886
Fixes #890

两单同落 service 页族、行集互斥(#886 是行为主张,#890 是技能能力主语),按 PM 认领评论并单一个 PR。基线 origin/main = 90686a4f,已含 PR #894(e10b6735,同页族 42 处 1:1 行改动)与 #885 / #892 / #895;这两个 PR 已写实的行本 PR 零回退,:101 附近的 emoji 锚点面(#866)与 zh 页 :138 链接缺锚点均未触碰。

纯文档改动:3 个页族 × 3 语言 = 9 个文件,加 1 个 changeset。未触碰 src/**、content/docs/releases/,没有任何 flow / hook / skill / 条件 / 收件人 / 字段发生变化。


一、前提复核(逐条重核于 90686a4f)

六处断言全部成立,无一失效:

# issue 断言 复核结果 证据
1 升级 start 条件全文只有 record.priority == "critical" ✅ 成立 src/flows/case-escalation.flow.ts:76-79,逐节点复核,全 flow 无第二个优先级项
2 insert 版孪生复用同一条件 ✅ 成立 case-escalation.flow.ts:155-165,只把 triggerType 换成 record-after-create
3 24 个 flow 无任何账户类型 / 分层条件 ✅ 成立 grep -rn "account_type|'customer'|tier" src/flows/ 仅 2 处无关注释(demo-bootstrap / opportunity-approval);pnpm validate 报 Logic: 24 Flows
4 src/ 无任何横幅机制 ✅ 成立 grep -rni "banner" src/ 全仓 1 命中,是 opportunity.object.ts:281 的无关注释
5 违约通知只发工单负责人一人 ✅ 成立 case-sla-monitor.flow.ts:84 recipients: ['{currentCase.owner_id}'],节点标签 Alert Owner
6 High 拿不到 sla_due_date ✅ 成立 case.hook.ts:60 if (priority === 'critical' && ...),其余优先级留空
7 case_triage 无检索工具 ✅ 成立 case-triage.skill.ts:49 tools: ['describe_object', 'get_record']
8 email_drafting 不取知识来源 ✅ 成立 email-drafting.skill.ts:21-37 五步 instructions 逐步复核,无一步取文章
9 文章匹配唯一归 customer_360 ✅ 成立 customer-360.skill.ts:48-52 query_records 查已发布 crm_knowledge_article,按 category / tags 匹配;六个 skill 逐一复核

一处补充测量(写进正文时据此收紧措辞):sla_due_date(case.object.ts:197)不是 readonly,人可以手工填,service.seed.ts 也为若干非 critical 种子工单显式写了值。所以正文写的是「没有任何自动化会给 High 打这个戳」,而不是「High 永远没有 SLA 截止日期」。

二、行号重定位表

issue 正文的行号是 PR #885 合并前的;本 PR 基线在 #885 / #894 之后。

#886

issue 行号(#885 前) 基线 90686a4f 本 PR 改后
sla :16 :16(未位移) :16
sla :57-60 :57-60(未位移) :57-64(该节由 4 行扩为 8 行)
sla :128 :134(#885 使正文 +6 行) :138

#890

issue 行号(0d3f3216) 基线 90686a4f 本 PR 改后
cases :156 :156(未位移) :156
index :48 :48(未位移) :48
sla :105 :105(未位移) :109(受本 PR :57 节 +4 行影响)

三语行号在每一格均一致。


三、#886 逐行改文

A. :57-60 —— 不存在的 High + Customer 升级分支

@@ -56,6 +56,10 @@
-The escalation flow runs the moment a case meets either of these conditions:
+The escalation flow runs on exactly one condition: **the case is at Critical priority**. Two flows carry that condition — `case_escalation` fires when a case is *edited* to Critical, `case_escalation_on_create` when a case is *created* Critical — and both read `record.priority == "critical"`, alongside the guards that keep an already-escalated, resolved or closed case from being escalated a second time.
 
-- The case is set to **Critical** priority, OR
-- The case is **High** priority **AND** the related account is a **Customer** (not a prospect)
+Two conditions this page used to list do not exist:
+
+- **There is no High-priority branch.** A High case is never escalated automatically. Raising one is a manual step — the **Escalate Case** button on the case record.
+- **No escalation condition reads the account.** Neither *Customer* nor *prospect*, nor any account tier, appears in any of them — no flow in this app reads an account's type at all. A Customer's High case and a prospect's High case are treated identically: neither escalates on its own.
+
+The time-based path does not catch them either. `case_sla_monitor` escalates any open case whose **SLA Due Date** has passed, but nothing stamps that date on a High case: the SLA hook fills `sla_due_date` for `critical` only and leaves it empty at every other priority, unless a person sets it by hand. So a High case nobody touches enters neither path — plan a human review pass for those rather than waiting on an automation.

对着的源码(src/flows/case-escalation.flow.ts:76-79):

condition: P`has(record.priority) && record.priority == "critical"
  && (!has(record.escalated_date) || record.escalated_date == null)
  && (!has(record.status)
    || (record.status != "escalated" && record.status != "resolved" && record.status != "closed"))`,

「High」「Customer」「潜在客户」「账户等级」四个词都点名保留并写明不存在,不静默删名;同时按 issue 要求把「High 拿不到 sla_due_date、兜底路径同样失效」一并写实,并给出可执行的替代(人工巡检 / Escalate Case 按钮)。未对 #595 预判——没有任何一句暗示 High 将来会自动升级或将来会有 SLA。

B. :16 —— Critical 违约行的红色横幅 + 支持经理

@@ -15,3 +15,3 @@
-| **Critical** | 4 hours | Red banner on case detail, alert to support manager |
+| **Critical** | 4 hours | The SLA monitor flags **SLA Violated**, escalates the case, and alerts the **case owner** by inbox + email. No red banner on the case detail page — this app has no banner mechanism — and no alert to a support manager: the notify node's recipient list is `{currentCase.owner_id}` alone |

「红色横幅」与「支持经理」两个词同样点名保留 + 写明不存在,真实归属(case-sla-monitor.flow.ts 的 notify 节点 → 工单负责人)写进同一格。4 hours 保留不动——case.hook.ts:61-63 确实给 critical 打 4 小时。

C. :134 → :138 —— 管理员提示里的同一分支复述

@@ -133,3 +137,3 @@
-- Escalation triggers (Critical, or High+Customer) live in the [Case Escalation flow definition](/docs/administration/automation#flows-multi-step). Adjust the condition there to change when escalation fires.
+- The escalation trigger — Critical priority, and nothing else; there is no High+Customer condition — lives in the [Case Escalation flow definition](/docs/administration/automation#flows-multi-step), i.e. `src/flows/case-escalation.flow.ts`. Two flows carry it: `case_escalation` (on update) and `case_escalation_on_create` (on insert). Change the condition in **both**, or a case created at the new priority still slips through.

顺带把管理员真正需要知道的那件事写进去:条件写在两个 flow 里,只改一个会漏掉新建路径。链接目标逐字保留——en 页保留 #flows-multi-step 锚点,zh-Hans / zh-Hant 页保留其原本没有锚点的 /zh-Hans|zh-Hant/docs/administration/automation,不顺手补锚点(悬空/缺失锚点是 #866 / #867 的面)。

紧邻的 :135→:139(#850 认领的「recipient lists ... configurable」那条)逐字未动,两单不踩线。

四、#890 逐行改文

三处的共同事实:case_triage 只带 ['describe_object', 'get_record'](无检索工具),email_drafting 五步 instructions 不取知识来源,真正读文章的是 customer_360。按 #861 / #865 的既有口径整句重写——检索归 Customer 360°,起草回复归邮件撰写,并说明分流两件都不做。

D. cases:156

@@ -155,3 +155,3 @@
-- *"Suggest a resolution"* — searches the [Support Knowledge Base](/docs/service/knowledge-base) and drafts a customer reply.
+- *"Suggest a resolution"* — no one skill does this end to end. **Case Triage** declares two tools, `describe_object` and `get_record`, and no query tool at all, so it searches nothing. Ask **Customer 360°** for the published [knowledge articles](/docs/service/knowledge-base) matched on the case's category or tags, then **Email Drafting** for the customer reply — the draft is grounded in the contact and account records, not in those articles, so paste in whatever it should quote.

E. index:48

@@ -47,3 +47,3 @@
-- *"Suggest a resolution"* — searches the support knowledge base and drafts a customer reply.
+- *"Suggest a resolution"* — two skills, not one. **Customer 360°** reads the published knowledge articles (`crm_knowledge_article` is an ordinary object it queries), and **Email Drafting** writes the customer reply. Case Triage does neither: it has no query tool, and no drafting step.

F. sla:105 → :109

@@ -104,3 +108,3 @@
-- The Copilot uses the [Support Knowledge Base](/docs/service/knowledge-base) to pattern-match against similar past cases.
+- **Case Triage does not** pattern-match against similar past cases. It declares `describe_object` and `get_record` only, so it reads the one case in front of it and can neither search [knowledge articles](/docs/service/knowledge-base) nor pull up the account's other cases. Matching published articles on a case's category or tags is **Customer 360°**'s job, and a separate ask.

该条所属清单的引子 :101([Service Copilot's Case Triage skill](/docs/ai-copilot/skills#case-triage),#866 的 emoji 锚点面)逐字未动;同清单的 :103 / :104 两条不在切割内,也未动。

链接标签处理:「Support Knowledge Base」是 #808 / #892 已清掉的虚构知识库名。三处链接保持指向 /docs/service/knowledge-base,标签改为该页真实标题——en 用 knowledge articles(对应页面 title: Knowledge Base 与 #892 落地的「唯一读文章的技能是 Customer 360°」口径),zh-Hans 用「知识文章」、zh-Hant 用「知識文章」,与 knowledge-base.zh-Hans.mdx:12、ai-copilot/service-copilot.zh-Hans.mdx:43 的既有称谓一致。

五、三语一致性与术语

zh 术语按语言包与页面既有称谓,未引入新词:

概念 zh-Hans zh-Hant 依据
crm_case 工单 工單 本页族既有称谓(#837 边界内,未改称呼)
case_triage 工单分流 工單分流 skills.zh-Hans.mdx:90、cases.zh-Hans.mdx:155
email_drafting 邮件撰写 郵件撰寫 service-copilot.zh-Hans.mdx:45、knowledge-bases.zh-Hans.mdx:56
customer_360 Customer 360° Customer 360° service-copilot.zh-Hans.mdx:50(带度符,#865 落地写法)
crm_knowledge_article 知识文章 知識文章 knowledge-base.zh-Hans.mdx:12、:41
站内消息 / 通知渠道 站内消息 站內訊息 本页 :68 既有
support manager 支持经理 支援經理 本页 :16 原词,写实时保留点名

六、验证输出(flock /tmp/os-heavy-verify.lock + NODE_OPTIONS=--max-old-space-size=4096)

步骤 命令 退出码
validate pnpm validate 0
typecheck pnpm typecheck 0
build pnpm build 0
test pnpm test -- --maxWorkers=2 0
lint pnpm lint 0
hygiene pnpm hygiene 0

关键行:

  ✓ Validation passed (1295ms)
  Data: 17 Objects  344 Fields
  Logic: 24 Flows                     ← 佐证「24 个 flow 无账户类型条件」的分母
  ✓ Build complete (1397ms)
  Artifact: dist/objectstack.json (1921.4 KB)

 Test Files  66 passed (66)
      Tests  1587 passed | 1 skipped (1588)
   Duration  45.82s

  13 warning(s), 14 suggestion(s) (1265ms)      ← lint,与基线同(均为既有 naming/namespace-prefix 与 field-group-shadowed)

Source hygiene — 245 files under src, test, e2e, scripts; the control-byte scan adds 409 under content, .changeset
  ✓ no console.log in src/
  ✓ no TODO/FIXME markers
  ✓ no raw control bytes in first-party files
  ✓ no source file over 100KB
✓ source hygiene clean

test 输出里出现的 ✗ source hygiene failed: ... / ✗ source hygiene: scanned director(y|ies) missing: content 是 test/source-hygiene-scan-surface.test.ts 元测试故意制造的 stderr(它验证扫描面缺失时会不会报错),属预期,测试整体 66/66 通过。

控制字节自扫(除 gate 之外,覆盖 gate 扫不到的 0x01-0x1f 位):

$ grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f]' <9 个改动文件> .changeset/service-behavior-residue.md
$ echo $?
1        # 零命中

七、changeset

.changeset/service-behavior-residue.md,'hotcrm': patch,两单分节,站内路径全部反引号包裹(#797)。

八、顺带发现(已按 Prime Directive #10 单独立单,未在本 PR 修)

两条都在本 PR 的切割之外,先对 #866 / #899 / #612 及全部 91 个 open issue 去重后新建,均未指派、未打标签,留给 PM 定级:


Generated by Claude Code

…and three skill subjects to source

Fixes #886
Fixes #890

#886 — content/docs/service/sla-and-escalation.mdx (+ zh-Hans / zh-Hant):

- The "High priority AND the related account is a Customer" escalation branch
  does not exist. `src/flows/case-escalation.flow.ts` gates the start node on
  `record.priority == "critical"` and nothing else; its insert-time twin
  `case_escalation_on_create` reuses the same condition. No flow in the app
  reads an account type or tier. Written real: only Critical auto-escalates,
  High never does, and raising a High case is a manual step. The SLA-monitor
  fallback does not cover them either — `case.hook.ts` stamps `sla_due_date`
  for `critical` only.
- The Critical breach row promised a red banner and an alert to a support
  manager. There is no banner mechanism under `src/`, and the breach notice is
  `case-sla-monitor.flow.ts`'s notify node, whose recipients are
  `{currentCase.owner_id}` alone.

#890 — service/cases.mdx:156, service/index.mdx:48,
service/sla-and-escalation.mdx:105 (+ zh-Hans / zh-Hant):

"Suggest a resolution" searching the knowledge base and drafting a reply, and
the Copilot pattern-matching past cases, are attributed to skills that cannot
do it: `case_triage` carries `describe_object` + `get_record` and no query
tool, and `email_drafting` never reaches for a knowledge source. The skill that
reads articles is `customer_360`. All three sentences rewritten on the #861 /
#865 line, and the fictional "Support Knowledge Base" link label replaced with
the page's real name.

Docs only, three pages x three locales, plus a changeset. No change under
`src/**`.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHrPAGEgFDoHjphqYG4BMa
@vercel

vercel Bot commented Aug 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
hotcrm Ignored Ignored Aug 6, 2026 5:47am

Request Review

@yinlianghui
yinlianghui marked this pull request as ready for review August 6, 2026 05:50
@yinlianghui
yinlianghui added this pull request to the merge queue Aug 6, 2026
Merged via the queue into main with commit dcec435 Aug 6, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment