Skip to content

docs(service): 把 service/index 的升级与通知五条说法按 flow / hook 写实(#904) - #913

Merged
yinlianghui merged 1 commit into
mainfrom
claude/issue-904-service-index-sweep
Aug 6, 2026
Merged

yinlianghui merged 1 commit into
mainfrom
claude/issue-904-service-index-sweep

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes #904

服务域的落地页 content/docs/service/index.mdx 及其 zh-Hans / zh-Hant 双生页,在「一个典型工单的生命周期」与「系统为你做的事」两处清单里,仍原样保留着 #876 / PR #885(sla 页)与 #850 / #887 / PR #894(cases 页)已经逐条写实过的那批说法。本 PR 只清这五处 ×3 语言 = 15 行。

开工前的 stale-premise 复核(基线 7df29789,平台 17.0.0-rc.3)

五条逐一对源码复核,全部仍然成立,行号在 fresh main 上也未被 PR #907 / #906 挪动(两者动的是同页 :48 与 zh-Hant 用字):

行 页面原话 源码事实
:30 升级流程「会将其重新分配给资深客服」,触发条件含「或来自客户」 src/flows/case-escalation.flow.ts 的 update_record 节点只写 is_escalated / escalation_reason / escalated_date / status,注释开头即 No owner reassignment;start 条件全文只有 record.priority == "critical" 加二次升级守卫
:36 自动升级「紧急工单或高优先级客户工单」给资深客服 同上;全仓 24 个 flow 没有任何一处读取账户类型
:37 「紧急时通知」给支持经理发邮件 recipients 只有 {caseRecord.owner_id} 一项;grep -rn "support_manager@" src/ 零命中
:38 「升级时通知」给升级团队发邮件 escalation_team@example.com 全仓零命中;状态转 escalated 触发的是 src/objects/case.hook.ts 的 case_status_side_effects 钩子,开出次日到期、紧急、归账户负责人的跟进任务
:40 SLA 违约「显示红色横幅」 grep -rn "banner" src/ 全 app 只有 src/objects/opportunity.object.ts 一句无关注释;违约的真实表现是 is_sla_violated 置真 + 升级 + src/flows/case-sla-monitor.flow.ts 的 notify 节点发给 {currentCase.owner_id} 一人的站内消息 + 邮件

口径

逐句对照 PR #885(service/sla-and-escalation 的 :53 / :57 / :61 / :62 / :78 / :92 / :93)与 PR #894(service/cases 的 :82 / :83)已落地的写法抄平,不引入第三种说法:读者来找的那几个词——资深客服、支持经理、升级团队、红色横幅——一律点名说清不做,并写出真实归属(升级只打标记 + 提醒工单负责人;跟进任务归账户负责人),而不是静默删名。优先级名沿用兄弟页的拉丁写法(Critical / High)。

明确不做的事

验证

守卫盲区如实说明:test/docs-drift.test.ts 与其余 66 个测试文件都不覆盖 content/docs/service/index*.mdx 的这段散文(grep -rn "service/index" test/ 零命中),所以本 PR 改前改后各门均为 predicted GREEN → 实测 GREEN,不存在「改完才变绿」的证据可拿。这是文本对照式改动,证据在上表的源码核对里,不在测试差分里。

串行于 /tmp/os-heavy-verify.lock,NODE_OPTIONS=--max-old-space-size=4096:

pnpm validate    → ✓ Validation passed (1311ms)          exit 0
pnpm typecheck   → tsc --noEmit                          exit 0
pnpm lint        → 13 warning(s), 14 suggestion(s)        exit 0   (全部为既有告警)
pnpm hygiene     → ✓ source hygiene clean                exit 0
pnpm build       → ✓ Build complete (1364ms)             exit 0
pnpm test --maxWorkers=2
                 → Test Files 66 passed (66)
                   Tests 1587 passed | 1 skipped (1588)  exit 0

推送前自扫控制字节:grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f]' 对三个 mdx 与 changeset 零命中。未启动任何 dev server。

纯文档改动,3 个文件 + 1 个 changeset,未触碰 src/**。


Generated by Claude Code

…ims to source (#904)

The service landing page still carried five claims that #876 / #885 (sla page)
and #850 / #887 / #894 (cases page) had already written to source: escalation
reassigns to a senior agent, a High + Customer branch, emails to a support
manager and to an escalation team, and a red SLA-breach banner.

Measured against `src/flows/case-escalation.flow.ts`,
`src/flows/case-sla-monitor.flow.ts` and `src/objects/case.hook.ts`:

- the escalation `update_record` node writes `is_escalated` /
  `escalation_reason` / `escalated_date` / `status` and never `owner_id`;
- the start condition is `record.priority == "critical"` alone, and no flow in
  this app reads an account's type;
- the only notify recipient is `{caseRecord.owner_id}`, and neither
  `support_manager@example.com` nor `escalation_team@example.com` occurs under
  `src/`;
- `grep -rn "banner" src/` finds one unrelated comment — there is no banner
  mechanism.

All five are rewritten in the wording the sibling pages already landed: name
what is NOT done and who really receives each thing, rather than deleting the
words a reader arrives looking for. The three claims the issue verified as true
(:35 / :39 / :41) are untouched, as is the skill-subject line #890 corrected.
The zh pages' link to the SLA page drops its English `#case-escalation` anchor,
which lands nowhere on a page with Chinese headings.

Documentation only, three files plus a changeset. No metadata changed.

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 7:57am

Request Review

@yinlianghui
yinlianghui marked this pull request as ready for review August 6, 2026 08:00
@yinlianghui
yinlianghui added this pull request to the merge queue Aug 6, 2026
Merged via the queue into main with commit 60dc7ed Aug 6, 2026
9 checks passed
This was referenced Aug 6, 2026
yinlianghui added a commit to yinlianghui/hotcrm that referenced this pull request Aug 10, 2026
…ycle alert to source (objectstack-ai#914, objectstack-ai#915) (objectstack-ai#922)

The cases page still carried the unswept twin of the five-step escalation list
(objectstack-ai#876 / objectstack-ai#885 wrote it real on the SLA page): a High+Customer trigger branch, a
reassignment to the agent's manager, a follow-up task on the original agent and
a three-party mailshot — none of which exist. Its priority-table sentence made
the same two claims 48 lines above the notify-node wording objectstack-ai#887 / objectstack-ai#894 had
already corrected on that very page.

Measured against src/flows/case-escalation.flow.ts and src/objects/case.hook.ts:
the start condition is `record.priority == "critical"` and nothing else, the
update_record node writes is_escalated / escalation_reason / escalated_date /
status and never owner_id, the follow-up task comes from the
case_status_side_effects hook and is owned by the ACCOUNT owner, and the notify
node's recipient list is the single entry `{caseRecord.owner_id}`.

The service index page's lifecycle step 2 carried the second copy of the
fictional support-manager recipient, ten lines above the line PR objectstack-ai#913 had just
written real. Its four priority names are aligned to the Latin spelling the
sibling pages use.

Documentation only, six files. No src/** change.


Claude-Session: https://claude.ai/code/session_01VHrPAGEgFDoHjphqYG4BMa

Co-authored-by: Claude <noreply@anthropic.com>
yinlianghui added a commit to yinlianghui/hotcrm that referenced this pull request Aug 10, 2026
…the real navigation (objectstack-ai#927) (objectstack-ai#932)

The section that tells a new user where to click named four items; three of
them did not survive a read of `src/apps/crm.app.ts`, and the one real item
the group does carry was missing.

Measured against `src/apps/crm.app.ts:131-140`, `group_service` has exactly
three children: `nav_case` (Cases), `nav_knowledge` (Knowledge) and
`nav_service_dashboard`, whose label is **Service Overview** — not "Service
Dashboard". `crm_task` has no entry in this group at all: its two nav items
are `nav_my_tasks` (My Tasks) and `nav_all_tasks` (All Tasks), both under
`group_work` / **My Work** (`:84` / `:93`). And no metadata anywhere carries
the name *Service Board*: the kanban is the view `case_workflow` with
`label: 'Service Workflow'` (`src/views/case.view.ts:72-75`), reached from
the **Workflow** tab in the case list's view switcher (`:59`) and never from
the sidebar.

So the list now names the three real items with their real labels, adds the
Knowledge entry it had been dropping, and keeps the two names readers will
arrive with — Tasks and Service Board — pointing at where those things
actually live, per the objectstack-ai#870 / objectstack-ai#877 / objectstack-ai#885 / objectstack-ai#894 / objectstack-ai#913 / objectstack-ai#924 convention of
naming what does not exist rather than deleting it silently.

Product questions are left open on purpose (objectstack-ai#595 / objectstack-ai#596): whether the kanban
should become its own nav item, and whether Tasks belongs under Service, are
not decided here — only the current shape is recorded.

All three locales, same lines. `src/` untouched.


Claude-Session: https://claude.ai/code/session_01VHrPAGEgFDoHjphqYG4BMa

Co-authored-by: Claude <noreply@anthropic.com>
yinlianghui added a commit to yinlianghui/hotcrm that referenced this pull request Aug 10, 2026
…to the real navigation (objectstack-ai#943) (objectstack-ai#953)

The section that tells a reader where to click named four things; three of them
do not survive a read of `src/apps/crm.app.ts`, and the one that does was
labelled with a name the app never shows.

Measured against `src/apps/crm.app.ts` on `origin/main` (b7791ca, platform
17.0.0-rc.3), the app has seven groups — Sales, My Work, Activity, Marketing,
Service, Insights, Approvals — and none of them is **Products**. The catalog's
only sidebar entry anywhere is `nav_product`, labelled **Products**, under
`group_marketing` (`:126`); `grep -rn "crm_product" src/apps/` returns that one
line. So the page now sends a reader arriving with the name "Products group" to
Marketing instead of to a place that does not exist, and links the Marketing
overview page that objectstack-ai#938 / PR objectstack-ai#942 just brought in line.

`group_approvals` (`:159-171`) has exactly one child: `nav_approval_requests`,
whose label is **Inbox** (`:165`) — not *Approval Requests*, which no metadata
in this repo carries. The other two listed items are re-judged individually
rather than deleted silently, per the objectstack-ai#870 / objectstack-ai#877 / objectstack-ai#885 / objectstack-ai#894 / objectstack-ai#913 / objectstack-ai#924 /
objectstack-ai#932 / objectstack-ai#942 convention:

- **Action History** — no nav item of that name exists. The data behind it does:
  @objectstack/plugin-approvals registers `sys_approval_action` (enumerated from
  the installed package, alongside `sys_approval`, `sys_approval_approver`,
  `sys_approval_delegation`, `sys_approval_request`, `sys_approval_token`), and
  nothing in this app's navigation opens it. The page says exactly that.
- **Processes** — deleted on purpose, and the source records why at `:166-169`:
  no `sys_approval_process` object exists in any installed plugin, so the old
  item's `requiresObject` guard hid it on every install, forever. The enumeration
  above confirms the absence, so the reason is written into the page.

The one true claim survives unchanged: **Contracts** is under **Sales**
(`nav_contract`, `:60`), and it is the app's only sidebar route to a contract.
Two further facts from source are recorded with it: `group_marketing` and
`group_approvals` declare no `expanded` key while Sales, My Work, Activity and
Service set `expanded: true`, and `GroupNavItemSchema.expanded` defaults to
`false` — so both groups are collapsed on load, which is the failure mode that
sends a reader looking for the catalog away empty-handed. And the zh-Hans page
names the label a simplified-Chinese user actually sees, 待我审批
(`src/translations/zh-CN.ts:1219`), next to the source label.

Product questions stay open on purpose (objectstack-ai#595 / objectstack-ai#596): whether Products deserves
its own group and whether Approvals should gain an audit-trail item are product
decisions, not documentation ones. Only the current shape is recorded.

All three locales, same lines; zh internal links carry no anchor. `src/`
untouched.

Fixes objectstack-ai#943


Claude-Session: https://claude.ai/code/session_01VHrPAGEgFDoHjphqYG4BMa

Co-authored-by: Claude <noreply@anthropic.com>
yinlianghui added a commit to yinlianghui/hotcrm that referenced this pull request Aug 10, 2026
… source (objectstack-ai#948) (objectstack-ai#955)

All four bullets of `content/docs/service/index*.mdx` "Standard dashboards &
reports" were wrong in all three locales, in two independent ways.

The dashboard bullet advertised a `top agents` tile and an `oldest open cases`
tile. `src/dashboards/service.dashboard.ts` ships ten widgets and neither is
among them — and neither is a widget nobody built yet: `case_metrics`
(`src/datasets/case.dataset.ts`) declares Status, Priority, Origin, Type and
Created as its only dimensions, so nothing in analytics can rank agents, and
every widget on the dashboard binds that dataset, i.e. aggregates it, so no tile
lists individual cases by age. `content/docs/service/cases.mdx:188` (objectstack-ai#912 / PR
objectstack-ai#939) had already written the agent half to source, so the two service pages
contradicted each other; this page was the one that was lying. The bullet now
names the ten real tiles and states why the other two cannot be built.

The three report bullets named labels that do not exist in
`src/reports/case.report.ts`: `Cases Opened by Day × Priority` inverts the two
dimensions of the real `Cases Opened by Priority × Day` (priority in `rows`, the
day in `columns` — `sla-and-escalation.mdx` already had the order right after
objectstack-ai#917 / PR objectstack-ai#924), `Cases by Status × Priority` spells `and` as `×`, and
`SLA Performance` drops the `Report` its label ends with. The SLA bullet also
still carried the "% of cases resolved within SLA target" claim PR objectstack-ai#924 removed
from the SLA page: no such measure exists — the report gives case count,
SLA Violation Rate and average resolution time by priority, over closed cases
only.

`test/docs-service-index-analytics.test.ts` pins both directions: every bolded
Latin name in the section must resolve to a real widget title, report label or
dataset label (phantom names stay in the *italics* this page already uses for a
name the product lacks, objectstack-ai#927 / PR objectstack-ai#932); every widget title must appear, so a
new tile cannot land while the summary goes stale; and the source side of both
negative claims is pinned too, so adding an agent dimension or an agent-ranking
tile fails here rather than silently making the prose wrong the other way.
Reverse-verified: restoring the four old lines turns 13 of the 20 assertions
red.

PR objectstack-ai#947's `Service Overview` reference and its first-mention `Customer Service`
annotation are untouched, as are the objectstack-ai#913 / objectstack-ai#922 / objectstack-ai#932 lines elsewhere on the
page. No metadata changed.

Claude-Session: https://claude.ai/code/session_01VHrPAGEgFDoHjphqYG4BMa

Co-authored-by: Claude <noreply@anthropic.com>
yinlianghui added a commit to yinlianghui/hotcrm that referenced this pull request Aug 10, 2026
… real app (objectstack-ai#960) (objectstack-ai#968)

The first table a new user reads, whose entire job is "here is what the sidebar
holds", had drifted in every one of its eight rows.

Measured against `src/apps/crm.app.ts` on `origin/main` (4705aed, platform
17.0.0-rc.3), the app's `navigation` is one pinned top-level entry plus seven
groups: `nav_home` (Home, :34), `group_sales` (:42), `group_work` (My Work,
:69), `group_activity` (Activity, :103), `group_marketing` (:120),
`group_service` (:131), `group_insights` (Insights, :144) and `group_approvals`
(:160). Row by row, the old table said:

- **Sales** — the group is real, but it carries nine children, not six. The
  table dropped Account Workbench (:52), Pipeline (:55) and Sales Performance
  (:61).
- **Service** — real; the entry the table called *Knowledge Base* is labelled
  **Knowledge** (:138), and **Service Overview** (:139) was missing. Both
  names were already written to source on `service/index` by objectstack-ai#927 / PR objectstack-ai#932
  and objectstack-ai#937 / PR objectstack-ai#947, so the two pages contradicted each other.
- **Marketing** — real, but its children are Campaigns (:125) and Products
  (:126). *Campaign Members* is not a navigation entry at all:
  `grep -rn "crm_campaign_member" src/apps/` returns nothing.
- **Products** — no such group. The catalog's only sidebar entry is
  `nav_product` under `group_marketing`, the same finding objectstack-ai#938 / PR objectstack-ai#942 and
  objectstack-ai#943 / PR objectstack-ai#953 already wrote to two other pages.
- **Activities** — no such group; the real one is **Activity**, and `crm_task`
  is not in it. Its two entries are My Tasks (:84) and All Tasks (:93), both
  under **My Work** — which the table never mentioned at all, though it is the
  group a rep uses every day.
- **Analytics** — no such group; the real one is **Insights**, and no entry
  anywhere is labelled *Dashboards* or *Reports*.
- **AI** — no such group anywhere in the repo, and no entry labelled *Copilot*
  or *Knowledge Bases*. `src/apps/` contains one file; neither name appears in
  it.
- **Approvals** — real, with exactly one child, labelled **Inbox** (:165).
  *Approval Requests* and *Action History* carry no metadata in this repo;
  objectstack-ai#943 / PR objectstack-ai#953 recorded the same two names on the revenue page.

So the table now lists the pinned Home entry and all seven groups with their
real children in source order, and every retired name is re-pointed rather than
deleted silently, per the objectstack-ai#870 / objectstack-ai#877 / objectstack-ai#885 / objectstack-ai#894 / objectstack-ai#913 / objectstack-ai#924 / objectstack-ai#932 / objectstack-ai#942
/ objectstack-ai#953 convention. Two further facts from source ride along: `group_marketing`,
`group_insights` and `group_approvals` declare no `expanded` key while Sales,
My Work, Activity and Service set `expanded: true`, and
`GroupNavItemSchema.expanded` defaults to `false`, so those three are collapsed
on load — the failure mode that makes a reader conclude something is absent.
And the zh pages name the labels a simplified-Chinese user actually sees
(待我审批, 知识库, 我的工作 — `src/translations/zh-CN.ts:1195-1219`), which is
also why the old English *Knowledge Base* read plausibly for so long.

Nothing checked any of it: `os validate` and `pnpm lint` walk authored metadata
and never open `content/docs`. `test/docs-quick-tour-navigation.test.ts` now
compares the table to `CrmApp.navigation` group-for-group and child-for-child in
all three locales, pins the bold-is-real / italic-is-phantom typography the
sibling pages already use, and pins the source facts the prose rests on.
Restoring the old table turns 15 of its 21 assertions red in the predicted
direction.

Product questions stay open on purpose (objectstack-ai#595 / objectstack-ai#596): whether Products or AI
deserve their own groups is a product decision, not a documentation one. Only
the current shape is recorded.

All three locales, same section; zh internal links carry no anchor. `src/`
untouched.

Fixes objectstack-ai#960

Claude-Session: https://claude.ai/code/session_01VHrPAGEgFDoHjphqYG4BMa

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants