Skip to content

docs(service): write the sla page's views, report dimensions and business-hours claims to source (#917) - #924

Merged
yinlianghui merged 1 commit into
mainfrom
claude/issue-917-sla-views-reports
Aug 6, 2026
Merged

yinlianghui merged 1 commit into
mainfrom
claude/issue-917-sla-views-reports

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes #917

把 content/docs/service/sla-and-escalation{,.zh-Hans,.zh-Hant}.mdx 上「页面指向的视图 / 报表 / 配置项是否存在」这一族的六处失实,按 origin/main(6014b2c, 平台 17.0.0-rc.3) 的源码逐条写实。口径沿同页 #885 / #894 / #907 / #918 已立的写法:点名说清不做什么 + 给出真实归属,不静默删名。src/ 零改动。

行号重定位

立单时的行号取自基线 7df29789;PR #918 之后本页已位移。开工前按 fresh main 逐条重定位(下表为英文页,三语同行号):

条目 立单行号 fresh main 实际行号
营业时间引用块 :23 :27
SLA 表现报表 :47-51 :47(引子) + :49-52(四个 bullet)
客服/经理节奏行 :117 / :118 :122 / :123
Waiting on Customer :125 :130
Breached SLA / Service Board 提示 :130 / :131 :135 / :136
营业时间管理员提示 :137 :142

#918 刚落地的 :12 / :30 / :32 族与 #907 的 :16 / :57 族均未触碰(diff 可核)。

逐条复核结论(六条 premise 全部成立)

1. 营业时间可配置 —— 不存在。 全仓 grep -rniE "business.?hour" src/ 只有三处产品目录/报价行描述文案;workingHours / businessCalendar / business_hours / slaCalendar 零命中。唯一算 SLA 时间的是 src/objects/case.hook.ts:60-63 的 due.setHours(due.getHours() + 4) —— 纯挂钟小时。引用块与管理员提示现在都点名说清没有这个设置,并给出后果(周五 16:00 建的 Critical 工单 20:00 到期,夜间/周末/节假日照算)。同时去掉了两处指向 Administration › Setup 的链接:它们唯一的用途就是那个不存在的营业时间设置。

2. SLA 表现报表四维度 —— 只有 Priority 是真的,另外三个语义层不可达。 src/reports/case.report.ts:25 的 sla_performance 是 rows: ['priority'];src/datasets/case.dataset.ts:15-21 的 case_metrics 只声明五个 dimension(status / priority / origin / type / created_date),无 owner/agent 维度、不跨对象取 crm_account.tier(该字段确实存在于 src/objects/account.object.ts)、created_date 的 dateGranularity 是 day 且不在该报表 rows 里。改写后逐个点名说明"为什么选不到",并按裁定③只写实「按优先级」,不预判平台应否支持更多维度。顺带写实同一句里的 runtimeFilter: { is_closed: true } 与实际度量 —— 报的是 SLA 违约率(avg_sla_violated),不是"目标内解决的百分比"。

3. Breached SLA / Critical Cases 列表视图 —— 不存在。 src/views/case.view.ts 的 crm_case 七个视图:all_cases / case_workflow / sla_calendar / case_timeline / my_open_cases / escalated_cases / sla_at_risk,无一以 is_sla_violated 过滤。全仓按违约过滤的只有 src/dashboards/service.dashboard.ts:144-148 的 SLA Violations 磁贴与 :115-116 的 Critical Cases 磁贴。节奏表与经理提示改指 Escalated Cases(case_sla_monitor 把它标违约的每一单都升级进去,是最接近"可逐条处理的违约列表"的东西)+ 那块磁贴,并明写两个旧名字不是视图。

4. Service Board 看板 —— 真名 case_workflow。 src/views/case.view.ts:72-75 的 label: 'Service Workflow',:59 的 tab 显示为 Workflow;Service Board 全仓不存在。

5. My Open Cases 排序 —— 主键是优先级。 src/views/case.view.ts:134-137 的 sort 是 priority_rank desc 在先、sla_due_date asc 仅作次级键。叠加 #903 已写实的事实(只有 Critical 会被打截止日期),Critical 以下各档次级键无值可排 —— 这一点也写进去了。

6. Waiting on Customer 暂停 SLA 时钟 —— 零实现。 写 sla_due_date 的只有 src/objects/case.hook.ts:60-63 一处,只在首次满足 critical 且字段为空时写一次;src/flows/case-sla-monitor.flow.ts:42-46 的 status: { $nin: ['resolved', 'closed'] } 不排除 waiting_customer(该状态值在 src/objects/case.object.ts:97)。所以挂在等客户回复上的 Critical 工单时钟照走、到点照样被标违约并升级。这一条改写后仍建议切状态(对团队有意义),但明写它不停表。

按裁定④,营业时间日历、按客服/账户等级的 SLA 分布、Waiting 停表是否应该存在,仍留给 #595,本 PR 只记录今天的行为。

验证

守卫盲区如实说明:本页这六处都是散文断言,仓内没有任何守卫扫描 content/docs/service/sla-and-escalation*.mdx 的正文(test/docs-drift.test.ts 的磁贴清单守卫只锚定 content/docs/analytics/dashboards*.mdx),因此预期方向就是 GREEN → GREEN,不存在"改前红、改后绿"的可测差分。唯一会因本改动移动的守卫是 docs-drift 的引用块数量三语平价(英/简/繁各 1 个 > 块,改写保持了块结构),以及 hygiene 的控制字节扫描(覆盖 content/ 与 .changeset/)。

pnpm validate   → exit 0   ✓ Validation passed (954ms);warning 均为既有项(approval/campaign_member)
pnpm typecheck  → exit 0   tsc --noEmit,无输出
pnpm lint       → exit 0   13 warning(s), 14 suggestion(s),均为既有项
node scripts/check-source-hygiene.mjs → exit 0
                ✓ no raw control bytes in first-party files(420 files under content, .changeset)
pnpm build      → exit 0   ✓ Build complete (1082ms)
pnpm test -- --maxWorkers=2 → exit 0
                Test Files  66 passed (66)
                Tests  1597 passed | 1 skipped (1598)

(test 输出里那几行 ✗ source hygiene failed: 是 test/source-hygiene-scan-surface.test.ts 在负向夹具上主动跑失败分支的正常回显,套件本身 66/66 绿。)

push 前控制字节自扫 grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f]' 三个文件,零命中。未起 dev server。


Generated by Claude Code

…ness-hours claims to source (#917)

Six claims on `service/sla-and-escalation` pointed at views, report
breakdowns and configuration switches this app does not ship:

- business hours configurable (callout + admin tip) — no calendar, no
  holiday list; `case.hook.ts` adds 4 wall-clock hours
- SLA Performance's four breakdowns — `rows: ['priority']` only, and
  agent / account tier / month are unreachable in `case_metrics`
- "Breached SLA" / "Critical Cases" list views — `crm_case` ships seven
  views, none filtering `is_sla_violated`; the breach filter lives on two
  Service Dashboard tiles
- "Service Board" kanban — the board is `case_workflow`, labelled
  "Service Workflow", tabbed as "Workflow"
- My Open Cases sorted by SLA Due Date — `priority_rank` desc is the
  primary key, due date only a tie-breaker
- Waiting on Customer pausing the SLA clock — nothing pauses it, and
  `case_sla_monitor`'s `$nin` does not exclude `waiting_customer`

Each keeps its name and gains the real attribution. Three locales; no
metadata under `src/` 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 9:03am

Request Review

@yinlianghui
yinlianghui marked this pull request as ready for review August 6, 2026 09:08
@yinlianghui
yinlianghui added this pull request to the merge queue Aug 6, 2026
Merged via the queue into main with commit 3b1e9fd 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
…ource (objectstack-ai#928) (objectstack-ai#933)

PR objectstack-ai#924 wrote the two business-hours promises on
`service/sla-and-escalation` to source. The same promise was still being
made in three other places, and the setup page had ended up
contradicting the SLA page outright:

- `administration/setup` section 2 — a checklist of three checkboxes
  under a bold "Setup → Business Hours" heading (working days, working
  hours, the year's holidays), closing with "business hours drive SLA
  calculations". No such screen, none of the three settings, and the
  page it linked to now says so
- `reference/faq` "My SLA clock isn't running" — listed "business hours
  are configured" and "the priority has an SLA defined" as
  preconditions, then promised a default SLA. All three are fictional;
  the first bullet (open status) was correct and is unchanged
- `reference/glossary` — the term was defined as "working days and hours
  used in SLA calculations"

Re-confirmed on `origin/main` first: `business_hours` / `workingHours` /
`businessCalendar` / `slaCalendar` have zero occurrences in `src/`, the
only `business.?hour` matches are three seed product descriptions, and
the only SLA deadline computed anywhere is
`due.setHours(due.getHours() + 4)` in `src/objects/case.hook.ts`, for
`critical` alone.

Each place keeps the name it used and gains the real attribution. Three
locales; no metadata under `src/` changed. Whether the app should grow a
business-hours calendar stays open in objectstack-ai#595.


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