Repository navigation
docs(service): write the cases and state-machine pages' remaining claims to source (#912, #920, #925, #926) - #939
Merged
Conversation
…ims to source (#912, #920, #925, #926) The state-machines page claimed enforcement it does not have: all five `state_machine` rules are `warning` severity, so an illegal move is logged and saved; the create path is not checked at all; nothing filters a status dropdown by the transition table; and no "bypass state machine" permission exists. The cases page carried the same claim (":78") plus three SLA statements that named a field that does not exist and a mechanism that was replaced, and a list-view roster where six of seven names were not views. Also corrects `workflow` where it named a metadata type removed in platform 7.7: the Contract Renewal Reminder automation is a flow. English, Simplified Chinese and Traditional Chinese. Docs only; `src/` untouched.
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
This was referenced Aug 6, 2026
yinlianghui
marked this pull request as ready for review
August 6, 2026 12:35
This was referenced Aug 6, 2026
Closed
Merged
Closed
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #912
Fixes #920
Fixes #925
Fixes #926
四单并单,收口
service/cases与administration/state-machines两页余下的失实说法,外加whats-new/guides/integrations两个卫星点。三语同步,src/零改动。零、stale-premise 复核(先证后改)
四单的行号基线早于 PR #919 / #922 / #924,全部在 fresh
origin/main(4855d50b)重定位;每处改动前逐条回源码复核。#920 的引擎侧机制(一次性探针,跑完即删,未留在仓库里)。探针直接调
evaluateValidationRules,即@objectstack/objectql/dist/core.mjs:2425-2443那条路径:violation 只有severity === 'error'才进 errors 数组并throw,其余走logger.warn。与 PR #919 的实录一致。三点值得单独记下:
cases:78那句原话的反例(New 直接到 Resolved)。ValidationError: Email is required、case 的 insert 那条抛了Resolution is required when closing a case—— 都不是状态机。把邻近规则的必填项补齐后,两条都变成NOT BLOCKED,只剩一条 WARN。把这类串扰当成「状态机拦下了」是最容易犯的误判。其余逐条复核:
grep -rn "initialStates" src/零命中(故 insert 路径直接return null);grep -rniE "bypass" src/无任何权限项;legalNextStates在node_modules/@objectstack/里唯一的消费者是runtime/dist/index.js:5834的GET /objects/{name}/state/{field}端点,本仓零调用;五条规则 severity 全部warning(lead:513/opportunity:414/case:347/contract:260/quote:264)。⛔ 按 #575 B4,没有把任何一条提到error。一、#920 —— state-machines 页的「护栏」说法(
:3:8:86-94:106:110-112:133:134)沿用 PR #919 已在本页
:22落地的口径(advice, not a gate/ 保存照样通过 / 引擎记 WARN),不新造措辞。:3frontmatterdeclares+ 「what the tables do and do not do」。这是搜索结果与导航里第一眼看到的句子:8:22:86-94:106validations[]条目,改它是代码改动加重新部署:110legalNextStates存在于平台、唯一消费者是那个 HTTP 端点、本应用无人调用:111:111无需再动(#921 与 #920 原文即写明「可以分别改也可以一轮改完」):133:110的孪生句,同一零实现。不改会让同页:110与:133自相矛盾,故一并收口:134grep零命中,且不需要 —— 没有东西要绕。新建路径根本不查表:112(新状态的必填字段)保留:那是requiredWhen谓词,是真的会拒绝保存的另一套机制(opportunity.object.ts:303/:327),只补一句点明它不是状态机。「What a state machine buys you」的初稿里我写了一条「Copilot 只建议转换表允许的那些转换」。写完去核,
grep -rniE "state.?machine|transition|legalNext" src/skills/ src/actions/零命中 —— 本仓没有任何技能或动作提到转换表。这句话我证不了,于是删掉重写成不对当前消费面做断言的说法(「流程条件、报表或技能可以按声明出来的路线来写」)。本页:114-122那一节本身(「Copilot 理解状态机…这可以防止 Copilot 建议非法的转换」)同属存疑,但它是平台侧 agent 行为、本仓证据不足以断言真伪,故不在本 PR 顺手改,另行归档为越界发现。二、#925 —— cases 页的三处 SLA 说法(
:59:70:71:111)口径抄平 #886 / PR #885 与 #903 / PR #918 已在
sla-and-escalation页落地的写法。:70SLA 截止日期:只对 Critical 成立(case.hook.ts:60-63,due.setHours(due.getHours() + 4)),且路径是beforeInsert加beforeUpdate—— 原文的「工单创建时」漏掉了「编辑为 Critical」这条路径,改写后两条都写到。High / Medium / Low 一个字节都不写。:71违约标记:真正写它的是case-sla-monitor.flow.ts的每小时扫描,选仍未结(status: { $nin: ['resolved','closed'] })且sla_due_date已过期的工单;从不比较解决时间与目标。两个后果(无截止日期永不是候选、晚解决但赶在下次扫描前解决永不被标记)照写。:59/:111字段名:真名是 SLA Violated(is_sla_violated,case.object.ts:205-209),breach flag不存在。:111「对客服只读」经核属实(service-agent.profile.ts:52-53两条 FLS 掩码readable: true, editable: false,resolution_time_hours同),只改字段名并补一句锁的来源。:27-32优先级表:按service/cases的《自动发生的事》:70/:71 是同族第三处未清扫的 SLA 说法——「SLA 截止日期按优先级计算」只对 critical 成立、「违约标记按解决时间超目标翻转」是另一套机制,字段名也不存在 #925 授权「一并判断」,在表下补一句「四行里只有 Critical 那行背后真有一只时钟」,与 sla 页:14同构。⛔ SLA policy matrix: per-priority × tier clocks, first-response stamping, escalation that reassigns #595 不预判 —— 只写当前行为,不谈 High/Medium/Low 该不该有时钟。三、#926 —— cases 页的视图清单与经理提示(
:114-121/:171-173,fresh main 上为:116-124/:174-176)case.view.ts的七个真实视图(All Cases / Service Workflow / SLA Calendar / Case Timeline / Escalated Cases / ⏰ SLA at Risk / My Open Cases),并按tabs数组写出每个视图在工单列表上对应的页签。逐条回源核过:kanban 的groupByField: 'status'加is_closed=false、calendar 的startDateField: 'sla_due_date'、timeline 的created_date → closed_date且groupByField: 'owner_id'。service.dashboard.ts:115/:144的磁贴(后者真名 SLA Violations),Service Board 的真身是 Service Workflow,Cases Due Today / Recently Closed / By Account 三个以任何形式都不存在。:171的「红色徽章」:工单列表上唯一的颜色规则是 All Cases 的rowColor,键在优先级上(case.view.ts:39-42),不是违约 —— 与service/sla-and-escalation的 SLA 跟踪一节仍有四处行为性失实:只有 Critical 拿得到 sla_due_date、违约标记并非只读、"实时倒计时"零实现、High/Medium/Low 的"违约长什么样"描述的其实是优先级行色 #903 已在 sla 页写实的同一条。:173客服排行榜:case_metrics(case.dataset.ts:15-21)只有 status / priority / origin / type / created_date 五个维度,无 owner 维度,所以按客服分组在语义层就做不出来。照 docs(service): write the sla page's views, report dimensions and business-hours claims to source (#917) #924 报表条的口径写实,不预判该不该加这个维度。四、#912 —— bare
workflows残留 4 处whats-new:89×3:contract_renewal是流程不是 workflow(src/flows/contract-renewal.flow.ts:25,label: 'Contract Renewal Reminder')。改成 flow 并与administration/automation:77内置流程表的标签逐字一致;中文两页沿用该表已有的「合同续约提醒」/「合約續約提醒」。这一项的实质(按每份合同自己的renewal_notice_days触发)复核属实(流程的check_notice_window决策节点按记录逐条判窗口),只纠叫法。state-machines:133(fresh main 上为:140)×3:Related 链接的「由转换触发的 workflows」改为「转换可以触发的流程与对象钩子」。cases:80×3:小节标题## Workflow automation改为## Flow and hook automation(中文「流程与对象钩子自动化」/「流程與物件鉤子自動化」),与该节四条正文(流程的notify节点、case_status_side_effects钩子)对齐,且不与同页## Case escalation等相邻标题撞名。guides/integrations:130×3(bareworkflows残留族在 #899 枚举之外还有 4 处:whats-new 把真实的contract_renewal流程叫成 workflow,state-machines:133 在 PR #894 扫过该文件后仍留着 #912 判为最弱一处,交接单人定夺):改。理由:该段虽是「设计意图(尚未落地)」,但它并列的两个代码面是现在时的事实陈述,而workflow命名的是平台 7.7 移除的元数据类型 —— 留着等于暗示除 flow / hook 之外还有第三种代码面。改为 flow / hook,一词之差,不动该段「尚未落地」的定性。五、PM 验收 #928 时追加的两行(出处:#928 报告的边界节)
以下两行不在四单原始面内,是 PM 在 #928 的 dev 验收中发现、判归本单一次收口的(同页、同口径,且紧邻
:174-176,hunk 自然连续)。照常先复核后改::166「New cases should not sit for more than the first-response SLA」—— 本仓不存在首次响应目标:first_response_date的唯一写入方是global.actions.ts:386-401的logActivityAction,写完之后没有任何东西读它去比对 ——case_metrics无首次响应度量(case.dataset.ts:23-27三个 measure 均与它无关),grep -rn "first_response" src/flows/零命中,无报表、无磁贴、无告警。按 docs: write the three business-hours claims outside the SLA page to source (#928) #933 已落地的「服务承诺而非应用跑着的时钟」口径降格。:167「it stops the SLA clock in some configurations」—— 与 docs(service): write the sla page's views, report dimensions and business-hours claims to source (#917) #924 已在 sla 页:138杀掉的是同一处虚构:sla_due_date只写一次、从不重算,case_sla_monitor的$nin只排除resolved/closed,waiting_customer不在排除名单上。同口径写实。六、边界(未触碰的东西,逐条自证)
administration/state-machines用的一整套状态词在src/里查无此值:lead 的 Working / Disqualified 是导入别名不是状态,opportunity 的 Closed Without Decision 根本不存在,Needs Analysis 被漏掉;:60的「Disallowed」举例反了 #921 的状态词行一字未动(R30 的单)。git diff -U0 content/docs/administration/state-machines.mdx的 hunk 起点为:3:8:86:88:90:94:106:110:133:140——administration/state-machines用的一整套状态词在src/里查无此值:lead 的 Working / Disqualified 是导入别名不是状态,opportunity 的 Closed Without Decision 根本不存在,Needs Analysis 被漏掉;:60的「Disallowed」举例反了 #921 点名的:14-15(清单两条)、:35-57(ASCII 图加转换表整表)、:60(Disallowed 举例方向)、:69(Closed Without Decision)、:118(Copilot 举例)全部不在任何 hunk 内。:8与:111两行按 PM 裁定归本单的行为面::8的第一句(含New/Working/Qualified三个词,顺序位置不变)逐字保留,R30 的词表替换仍是干净的一处改动。state-machines:12-24整节(含:22的 advice-not-a-gate 段)、cases:34/:82-83/:89-105/:181全部原样,不在 diff 内。:79-84(「The system also enforces」四条商机必填/审批)未改 —— 不在administration/state-machines通篇声称状态机会「拦下」非法转换,实测五条规则全是warning:保存照样通过;连「只显示合法下一状态」「bypass state machine 权限」都无实现 #920 的裁定面内,另行归档为越界发现。src/**零改动;@objectstack/*版本未动;本仓无content/docs/releases/(whats-new不是发布说明目录,且 bareworkflows残留族在 #899 枚举之外还有 4 处:whats-new 把真实的contract_renewal流程叫成 workflow,state-machines:133 在 PR #894 扫过该文件后仍留着 #912 明确其为「纠叫法不是改历史」)。/zh-Hans/docs/...//zh-Hant/docs/...)。docs-drift.test.ts的 callout 平价守卫要的就是这个等式。七、验证
六道门全部在
flock -w 7200 /tmp/os-heavy-verify.lock内串行,NODE_OPTIONS=--max-old-space-size=4096:pnpm validateData: 17 Objects 344 Fields;5 条 author-time 警告与 main 同(approval / campaign_member group)pnpm typechecktsc --noEmit,无输出pnpm lint13 warning(s), 14 suggestion(s),与 main 同pnpm hygiene✓ no raw control bytes in first-party files;扫描面含content与.changeset(425 个文件)pnpm build✓ Build complete;dist/objectstack.json (1921.4 KB)pnpm test -- --maxWorkers=2Test Files 70 passed (70);Tests 1602 passed, 1 skipped (1603)单跑 #919 新增的守卫(本页唯一读
content/docs的钉子):test/status-state-machines.test.ts得到Test Files 1 passed (1)/Tests 39 passed (39)。守卫覆盖面的诚实交代 —— predicted GREEN, stayed GREEN。 该守卫只读本页首个
##小节的列表项(:12-18的对象清单),而本 PR 一个字都没动那一节,所以它预测就应该保持绿,跑出来也确实是绿 —— 这证明我没碰坏清单,不证明新写的散文是真的。本 PR 改的其余全部行都在守卫盲区里:grep -rn "content/docs" test/命中的几个文件分别钉 automation 页的流程表、contacts 页的邮箱唯一性、dashboards 页的磁贴名、三语 callout 计数与「转化率」用词,没有一个读 cases / state-machines / whats-new / integrations 的散文。撑住这些散文的是上面第零节的探针实录与逐条源码引用,都写进了正文,下一个读者可以照着复核。push 前另做控制字节自扫(覆盖
check:nul-bytes之外的整段范围):改动的 12 个 mdx 加 changeset 零命中。未起 dev server;探针为一次性文件,跑完已删,git status干净。Generated by Claude Code