Skip to content

service/sla-and-escalation 的 SLA 跟踪一节仍有四处行为性失实:只有 Critical 拿得到 sla_due_date、违约标记并非只读、"实时倒计时"零实现、High/Medium/Low 的"违约长什么样"描述的其实是优先级行色 #903

Closed

Description

@yinlianghui

发现自 #886 / #890 的实施过程(PR 见下)。#886 的裁定面被定死在 :16(Critical 违约行)、:57-60(升级触发条件)、:128→:134(管理员提示),本条是同页另外一族说法——SLA 是怎么被计算和被跟踪的——不在那个切割里,故单独立单,未在该 PR 里顺手改。

基线 origin/main = 90686a4f(含 PR #885 / #894 / #895)。行号为该基线上的原始行号;PR #886/#890 使 :57 一节增长 4 行,故其后的行号在该 PR 合并后 +4。

事实

A. :12 —— "每个工单在创建的那一刻就会获得一个 SLA 截止日期"

content/docs/service/sla-and-escalation.mdx:12(zh-Hans / zh-Hant 同行号):

> Every case gets an SLA due date the moment it's created. The target is based on priority:

src/objects/case.hook.ts:60-64 只为 critical 打这个戳:

if (priority === 'critical' && !input.sla_due_date && !ctx.previous?.sla_due_date) {
  const due = new Date();
  due.setHours(due.getHours() + 4);
  input.sla_due_date = due.toISOString();
}

其余三个优先级一律留空。该字段(src/objects/case.object.ts:197)不是 readonly,所以人可以手工填,src/data/service.seed.ts 也确实为若干非 critical 的种子工单显式写了值——但没有任何自动化会给它们打戳。

这一条现在与同页的另一处直接打架:PR #886/#890 按 #886 的裁定,在"何时触发升级"一节写实了"SLA 钩子只为 critical 填 sla_due_date,其余优先级一律留空"。:12 与它相隔 45 行,说的是反话。两处必须由同一个人一次改齐,这也是本单立在这里的主要原因。

B. :32 —— "违约标记对客服是只读的——他们无法篡改"

> The breach flag is read-only for agents — they can't fudge it. It's auto-calculated by the system.

src/objects/case.object.ts:205-210 明确不是 readonly,且注释写明了为什么不能是:

// NOT `readonly`: the case_sla_monitor flow stamps this, and 16.x drops
// writes to readonly fields (#2948) — readonly here silently disabled
// SLA violation tracking.
is_sla_violated: Field.boolean({ label: 'SLA Violated', group: 'sla', defaultValue: false }),

它同时出现在 src/views/case.view.ts:28 的列表列与 src/pages/case_detail.page.ts 的字段面上。也就是说:一个有 crm_case 编辑权的客服可以直接把它改掉,页面承诺的那道门不存在。("由系统自动计算"那半句是对的——case_sla_monitor 会写它。)

C. :34-38 —— "工单详情页在页眉中显示实时 SLA 倒计时"

> The case detail page shows a live SLA countdown in the header:
> - ⏱ 2h 14m remaining — fine
> - ⚠️ 24m remaining — warning zone
> - 🚨 BREACHED – 1h 32m over — escalation territory

grep -rni "countdown\|remaining" src/ 在整个 app 里零命中(两处 remaining 是 dataset / dashboard 里的无关注释)。src/pages/case_detail.page.ts:61-68 的注释自己写着 crm_case 只带一个 sla_due_date 加派生字段——页眉里是这两个字段的普通渲染,没有任何倒计时组件、阈值配色或"超时多久"的计算。#886 的正文已记过这一处,但把它排除在该单的切割之外。

D. :17-19 —— High / Medium / Low 那三行的"违约长什么样"

> | High | 8 hours | Red badge in list views |
> | Medium | 2 business days | Amber badge |
> | Low | 7 business days | Grey badge |

src/views/case.view.ts:39-42 里唯一的颜色声明是 all_cases 视图的 rowColor,而且它键在 priority 上,不是键在违约上:

rowColor: {
  field: 'priority',
  colors: { critical: '#dc2626', high: '#f97316', medium: '#eab308', low: '#94a3b8' },
},

三重不符:(1) 它是行底色,不是徽章;(2) 它按优先级恒常渲染,跟有没有违约无关——一个刚建、离截止还有 8 小时的 High 工单就已经是这个颜色了;(3) 连颜色都对不上——High 是橙色 #f97316 而不是红色,Medium 是黄色 #eab308。列表视图里真正表示违约的是 is_sla_violated 这一列(case.view.ts:28)。同时这三行的目标值(8 小时 / 2 个工作日 / 7 个工作日)在 A 的意义上也不是系统会盯的截止时间,而是团队的服务承诺。

为什么要紧

A 与 B 是会让人做错事的两条:一个服务经理读完 A,会以为所有工单都在 SLA 时钟上跑,于是不给 High 工单排人工巡检(#886 修的升级面已经写实了这一点,但 :12 还在反着说);读完 B,会以为违约率这个指标防篡改,于是拿它做考核。C 与 D 是纯粹的界面失实,代价是读者按图找不到东西。

边界

Refs #886 #890 #595 #788

Activity

  1. added
    pm:queueReady for the PM dispatch loop
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Aug 6, 2026
  2. self-assigned this
    on Aug 6, 2026
  3. yinlianghui commented on Aug 6, 2026

    @yinlianghui
    CollaboratorAuthor

    🔒 认领(/pm-dispatch R27)— session session_01VHrPAGEgFDoHjphqYG4BMa,分支 claude/issue-903-sla-tracking-fidelity。

    文件面:content/docs/service/sla-and-escalation{,.zh-Hans,.zh-Hant}.mdx,仅 A(:12)/ B(:32)/ C(:34-38)/ D(:17-19)四族——行号为基线 90686a4 原始值,PR #907(#886+#890)与 #906(#844)已动同页,fresh main 上重定位。

    裁定:

    1. 基线已切 17.0.0-rc.3(PR chore(deps): 升级 @objectstack/* 17.0.0-rc.2 → 17.0.0-rc.3(零后果修复),并实测四条在案上游镜像 #910),开工先 stale-premise 复核(rc.3 唯一实质变更为 BulkActionParamSchema passthrough,与本单无关,预计前提全部成立,仍须逐条核)。
    2. A 条与 service/sla-and-escalation 三语还剩两处行为性失实:升级触发条件多出一个不存在的 High+Customer 分支,Critical 违约行承诺「红色横幅 + 通知支持经理」 #886 已写实的「钩子只为 critical 填戳」句必须一次改齐,消除同页自相矛盾;service/sla-and-escalation 三语还剩两处行为性失实:升级触发条件多出一个不存在的 High+Customer 分支,Critical 违约行承诺「红色横幅 + 通知支持经理」 #886/service 三页仍写「建议一个解决方案」会检索知识库并起草回复 / Copilot 用知识库对相似历史工单做模式匹配——两个技能都做不到 #890 已落地的 :16/:57 族/:105/:134 改动不得回退。
    3. 口径沿 docs(automation): 按 flow 源码写实内置流程表两行,并修 opportunity_won_alert 的 description (#851) #870/docs(flows): name what the flow nodes do, not what they were once meant to do #877/docs(service): 按 flow 与 hook 写实 SLA 页的升级说明(#876) #885/docs: 清扫 automation 页之外的「工作流规则」并写实 cases 两条虚构通知 (#850, #887) #894:点名说清不做什么 + 真实归属,不静默删名。
    4. SLA policy matrix: per-priority × tier clocks, first-response stamping, escalation that reassigns #595 不预判:只写实当前行为,不判断 High/Medium/Low 应否有 SLA 时钟、违约标记应否只读。
    5. B 条只改页面承诺,不动字段定义(readonly: true 在 insert 路径上完全不生效,且 update 路径的剥离会连 hook 自己写的戳一起删掉 —— 已发布文章可落成 published_at = null #788 平台侧语义无关)。

    Generated by Claude Code

  4. yinlianghui commented on Aug 6, 2026

    @yinlianghui
    CollaboratorAuthor

    实施完成,draft PR #918。四族里 A / C / D 三族前提成立并已写实,B 族前提复核后证伪 —— 详情与证据见 PR 正文,这里只记 B 的结论,因为它改变了本单的结果面。

    B :32「违约标记对客服是只读的」—— 不是失实

    本单的依据是 src/objects/case.object.ts:205-210 不带 readonly,推论「页面承诺的那道门不存在」。字段确实不是 readonly(注释写明原因),但门在另一层:

    src/profiles/service-agent.profile.ts:52
      fields: { 'crm_case.is_sla_violated': { readable: true, editable: false }, … }
    

    fields 是 PermissionSetSchema 的一等键(PERMISSION_SET_KEYS 含 fields;FieldPermissionSchema = { readable, editable },.strict()),不是会被静默丢弃的未识别键。本仓自己的文档也已列过这条遮罩 —— content/docs/administration/profiles.mdx:82:「crm_case.is_sla_violated / resolution_time_hours | Service agent | Nobody — they are computed」;sharing-and-security.mdx:194 写 FLS「enforced everywhere」。其余 profile 里 sales_manager / sales_rep 对 crm_case 本就 allowEdit: false,marketing_user 无授权,guest_portal 只能 insert。所以「对客服只读」这句是对的,照本单原样改写会把一句正确的话改成新的失实。

    处理:保留该判断,补写真实机制(字段级安全而非字段属性)并链到 Profiles 的 FLS 表。

    B 这一族真正失实的是相邻的 :30,字段定义行:「SLA Breached? — true when the resolution time exceeds the target」。字段标签是 SLA Violated,不存在叫 SLA Breached? 的字段;置真条件也不是「解决时间超过目标」,而是每小时扫描命中「status 不在 resolved/closed + sla_due_date 已过」(case-sla-monitor.flow.ts:40-47)。这条已按裁定 2 的口径改写,并与 A 咬合(没有截止日期的工单永远不入选)。

    行号重定位

    四族在 fresh main(7df29789)上未位移,:12 / :17-19 / :32 / :34-38 逐一原址命中 —— PR #907 / #906 的增删都落在 :57 一节及其后。

    顺带按裁定 1 一次改齐的同族回声

    • :8 导语(「gives every case a deadline … surfaces breaches in real time」)是 A 与 C 的同一句话换位置。
    • :124 给客服的提示(「关注你的 SLA 倒计时——当你看到琥珀色区域时」)是 C 的回声。
    • :21(「SLA 目标可按优先级配置」)随 A / D 收窄。

    只改被点名的四行会把矛盾留在更显眼的位置,故一并改。#886 / #890 已落地的 :16 / :57 族 / :105 / :134 逐字未回退。

    另一族失实已单独立单

    施工中发现同页另一族(页面指向的视图 / 报表 / 配置项是否存在)6 处失实,行集与本单互斥,已先搜重复后立单 #917,未在本 PR 顺手改。


    Generated by Claude Code

  5. yinlianghui commented on Aug 6, 2026

    @yinlianghui
    CollaboratorAuthor

    ✅ 验收通过 —— PR #918 已转 ready 并挂 auto-merge(CI 8/8 全绿实核,head a036d7a)。

    验收要点:

    1. premise partial 是本单最有价值的产出:A/C/D 三族成立(C 的证据比 issue 更强——页眉根本没有 SLA 字段,两个字段渲染在 record:highlights 的 Key Information 里);B 族被证伪:「违约标记对客服只读」这道门确实存在,只是实现在 service-agent.profile.ts:52 的字段级安全('crm_case.is_sla_violated': { editable: false },FieldPermissionSchema 一等键非静默丢弃,profiles.mdx:82 与 sharing-and-security.mdx:194 互证)而非字段属性——照 issue 字面改写会把一句正确的话改成新失实。dev 保留正确判断、补写真实机制,并连带改写了 B 族里真正失实的相邻 :30(标签名 SLA Violated、置真条件是每小时扫描而非「解决超时」、两个边界后果与 A 咬合)。
    2. 裁定 1-5 全遵守:A 与 service/sla-and-escalation 三语还剩两处行为性失实:升级触发条件多出一个不存在的 High+Customer 分支,Critical 违约行承诺「红色横幅 + 通知支持经理」 #886 已写实句一次改齐,同族回声 :8/:21/:124 一并收口;service/sla-and-escalation 三语还剩两处行为性失实:升级触发条件多出一个不存在的 High+Customer 分支,Critical 违约行承诺「红色横幅 + 通知支持经理」 #886/service 三页仍写「建议一个解决方案」会检索知识库并起草回复 / Copilot 用知识库对相似历史工单做模式匹配——两个技能都做不到 #890 已落地行逐字未回退;四个名字(SLA Breached?/红色徽章/琥珀区/倒计时)全部点名说清「不存在/是别的东西」,无一静默删名;SLA policy matrix: per-priority × tier clocks, first-response stamping, escalation that reassigns #595 一句未评并在 changeset 显式指回;src/ 零改动。
    3. 行号四族在 fresh main 原址命中(docs(service): 把升级触发条件、Critical 违约行与三处技能能力主语按源码写实 (#886, #890) #907/docs(i18n): spell the lead convert verb 转化 on every Chinese page (#844) #906 的增删都落在 :57 节及其后),重定位结论可靠。
    4. 守卫盲区(散文列)如实报 predicted GREEN,证据是逐条 file:line;dev 已自行在 PR 评论更正正文引用号(docs: 清扫 bare workflows 残留族 —— 引擎里没有「重新评估 5 次」这个计数器,报价对象上也没有可调的计划 (#899) #916→docs(service): SLA 跟踪一节按实测行为改写(#903) #918),处置正确。

    越界发现处置:#917(同页第三族六处:营业时间、报表维度语义不可达、Breached SLA 视图、Service Board 名、排序键、Waiting 暂停时钟)已复核成立且行集与本单互斥,标 pm:queue;⚠️ 与 #913/#918 同页,派发须在 #918 合并后重定位,R28 候选。


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

pm:dispatchedDispatched to a dev agent by /pm-dispatch

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions