Skip to content

[观察] crm_case 在中文文档里有三种叫法:语言包「服务案例」、25 个页面「工单」、14 个页面「案例」(两页同页混用) #837

Description

@yinlianghui

来源:#827(PR 补全 automation 页的内置 flow 表)实施过程中的顺带发现。基线 origin/main = 4c12791。观察类,未打 pm:queue —— 今天没有任何功能因此坏掉,是一致性/可搜索性问题。

事实(zh-Hans,zh-Hant 同形)

同一个对象 crm_case,三个面各说各的:

面 叫法
语言包 src/translations/zh-CN.ts:578 label: '服务案例' / pluralLabel: '服务案例'
25 个 *.zh-Hans.mdx 工单(含 service/cases、service/sla-and-escalation、administration/sharing-and-security、administration/profiles)
14 个 *.zh-Hans.mdx 案例(含 administration/automation、administration/state-machines、sales/pipeline-management)

两页在同一文件内混用:content/docs/reference/faq.zh-Hans.mdx、content/docs/getting-started/quick-tour.zh-Hans.mdx。

test/sharing-coverage.test.ts 的 ROW_LABEL 里 crm_case 记的是 工单 / 案件(那张表的口径),而 automation 页整页用「案例」—— 两张守卫过的表因此对同一对象给出不同的行标签,各自都是绿的。

为什么现在没修

#827 的 PR 重写了 automation 三语 flow 表的 24 行标签,本来是统一口径的机会,但:产品 UI 里用户看到的是语言包的「服务案例」,两种文档写法都不等于它;选哪一个是产品用词决策,不该在一个补表格的 PR 里顺手定。而只改 automation 一页会当场造出新的页间漂移(那页要么和 25 个「工单」页不一致,要么和自己上下文里其余「案例」不一致)。

修法建议

先定调用哪个词(建议以语言包的 label 为准,文档跟随产品 UI),再做一次全树替换,并考虑把「文档用词 = 语言包 label」做成守卫 —— 这类漂移目前对所有测试不可见。

Refs #827 #810

Activity

  1. huangyiirene commented on Aug 31, 2026

    @huangyiirene
    Collaborator

    裁决:B —— crm_case 全线统一「工单」(维护者 2026-08-31)

    项目总监席 · session session_01KGtaLpkW1mycWgkbSb3H6t · hotcrm 决裁批 #16 ④

    维护者原话(逐字):「crm_case 统一叫工单。」

    裁决内容

    1. 产品用词定为「工单」:四个语言包中 crm_case 的 label(现「服务案例」)改为「工单」—— 界面随语言包同步;
    2. 文档统一:14 个用「案例」的页面改「工单」;25 个已用「工单」的页面保持。⛔ 同页混用(faq、quick-tour)清零;
    3. 守卫同笔落(呈报 B 的组成部分):加「文档用词 = 语言包 label」一致性守卫,使 sharing-coverage 的 ROW_LABEL 与 automation 页这类「两表各绿、互相矛盾」的漂移下次在编写期即红 —— 这类漂移今天对所有测试不可见,守卫是治本的一半;
    4. 改动面按呈报实测:4 语言包 + 14 页,小于 A 的 39 页。

    状态转移(同笔)

    needs-user-decision → pm:queue。


    Generated by Claude Code

  2. added
    pm:queueReady for the PM dispatch loop
    and removed
    needs-user-decisionNeeds the maintainer's call before work proceeds
    on Aug 31, 2026
  3. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Sep 3, 2026
  4. self-assigned this
    on Sep 3, 2026
  5. os-sales commented on Sep 3, 2026

    @os-sales
    Collaborator

    Claim: PM loop round R29
    Session: session_019hUuCQStzXGMFSX4dzww5t
    Branch: claude/issue-837-crm-case-gongdan
    Worktree: hotcrm-issue-837
    Domain: repo:hotcrm execution seat (single-lane repo — ⛔ no domain:*)
    File surface: src/translations/{en,es-ES,ja-JP,zh-CN}.ts, the zh doc pages under content/docs/** that carry the term, test/sharing-coverage.test.ts (ROW_LABEL), plus wherever the new consistency guard lands (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: opus (⛔ fable exhausted 2026-09-02, floor opus per the standing exemption)
    Clause-②: no
    Serial constraints cleared: src/translations/** fence lifted — #781 landed as PR #1519 (e8a3ae9). #1511 (sla_overview) and #1199 (inert fields whose only carriers are locale rows) share these files, are pm:queue and undispatched; ⛔ neither may be dispatched until this lands. content/docs/** overlaps the queued docs cluster (#1410 · #1397 · #1395 · #1402 · #1422) — all undispatched. No open PR touches these paths.

    ⚠️ Premise re-derived on origin/main @ e8a3ae9, and the card's counts have MOVED — ⛔ re-derive, never transcribe, when the card's subject is drift:

    card says measured now
    25 pages use 「工单」 29
    14 pages use 「案例」 14 ✅
    2 pages mix both (faq, quick-tour) 5 by my read — accounts, setup, glossary, knowledge-base, quick-tour

    ⚠️ My mixed-page probe enumerates files containing 「工单」 that also contain 「案例」, so it structurally cannot see a page carrying only 「案例」 — faq is named by the card and absent from my list, which means my 5 and the card's 2 are not the same measurement. ⛔ Neither number is authoritative; the dev re-derives both directions.

    Still true: zh-CN.ts:652-653 carries label: '服务案例' / pluralLabel: '服务案例', and test/sharing-coverage.test.ts:574 carries the fourth spelling (zh-Hans: '工单', zh-Hant: '案件'). ⭐ Note zh-CN.ts:738 already has case: { label: '工单' } under a different key — so the target term is already in the locale in one place, which the dev should reconcile rather than duplicate.


    Generated by Claude Code

  6. os-sales commented on Sep 3, 2026

    @os-sales
    Collaborator

    Claim (dev seat, separate from the PM claim above).

    Session: session_019hUuCQStzXGMFSX4dzww5t
    Branch: claude/issue-837-crm-case-gongdan
    Worktree: hotcrm-issue-837
    Base: origin/main @ e8a3ae9

    Ordering note, declared rather than hidden: the empty branch went up first as the write-route probe, and this claim comment landed after implementation rather than before it. The branch named here is the same one the probe created, and no other session touched these paths.

    Assignee deliberately untouched.

    Generated by Claude Code


    Generated by Claude Code

  7. os-sales commented on Sep 3, 2026

    @os-sales
    Collaborator

    os-dev-report

    {
      "issue": 837,
      "status": "done",
      "branch": "claude/issue-837-crm-case-gongdan",
      "pr": "https://github.com/objectstack-ai/hotcrm/pull/1525",
      "premise_still_valid": true,
      "summary": "The card's core premise held and understated itself: crm_case answered to FIVE Chinese names, not three. Unified the whole Chinese surface on 工单 / 工單 — zh-CN pack (6 places: label, pluralLabel, description, case_number, nav_case, knowledge_article.related_to_case), 33 doc pages (15 zh-Hans, 18 zh-Hant), and the 4 authored test ledgers that pinned an older spelling. Added the ruled guard, test/docs-object-term-consistency.test.ts, which derives the term from the pack and fails on a retired spelling in a page, in the pack, or in a string literal in any test ledger. TWO PM/card mechanism assumptions falsified: (1) 'the four locale packs' label is 服务案例' — only zh-CN carries a Chinese term (en=Case, es-ES=Caso, ja-JP=ケース), so writing 工单 into four packs would have put a Chinese word on three non-Chinese UIs; changed zh-CN only. (2) 'zh-Hant 同形' — false three ways: 工单 is Simplified-only (Traditional is 工單, invisible to a Simplified grep), zh-Hant carries a spelling zh-Hans does not (案件, 3 pages, matching sharing-coverage's ROW_LABEL), and zh-Hans carries one zh-Hant does not (个案, whats-new). Change surface is 33 doc pages against the ruling's 14.",
      "counts_rederived_on_e8a3ae9": {
        "card_said": "25 pages 工单 / 14 pages 案例 / 2 mixed",
        "pm_said": "29 / 14 / 5-by-a-partial-probe",
        "measured_zh_Hans": "29 工单 · 14 案例 · 1 个案 · 5 mixed",
        "measured_zh_Hant": "27 工單 · 15 案例 · 3 案件 · 5 mixed",
        "both_directions": "mixed pages are the same 5 slugs in both scripts (accounts, setup, glossary, knowledge-base, quick-tour); faq — named by the card — carries only 工单 in Simplified and only 案件 in Traditional, so it was never a mixed page by term, which is why the card's 2 and the PM's 5 were different measurements",
        "spellings_found": 5,
        "chinese_hits_read": 92,
        "hits_rejected_as_unrelated": 1,
        "rejected_detail": "src/translations/ja-JP.ts:566 — crm_opportunity description 「パイプライン上の商談・案件」, Japanese, different object. Zero 案例 hits in the Chinese docs were 'case study' prose.",
        "doc_pages_changed": 33,
        "locale_packs_changed": 1
      },
      "guard_observed_failing": true,
      "guard_ablation": {
        "file": "test/docs-object-term-consistency.test.ts (12 rules)",
        "A_whole_pre_fix_tree": "7 of 12 red",
        "B_pre_fix_zh_CN_pack_only": "4 red",
        "C_pre_fix_sharing_coverage_ledger_only": "2 red — the ledger rule; the case the ruling names",
        "D_pre_fix_automation_zh_Hans_page_only": "1 red",
        "E_term_deleted_from_all_39_zh_Hans_pages": "1 red — 'only 0 zh-Hans page(s) name crm_case as 工单'",
        "mutation_proof": "each leg asserted the mutation on disk before running (git diff HEAD name-count and alias hit-count; leg E counted 557 occurrences replaced across 39 files)",
        "restore_proof": "each leg restored with git checkout HEAD -- ABSOLUTE_PATH under an EXIT/INT/TERM trap, then proved by git diff HEAD naming 0 files AND the guard's git hash-object equalling its HEAD blob (c0a0998...) — empty hash treated as failure",
        "no_op_recorded": "leg E's FIRST attempt was a no-op: a perl substitution matched nothing, exited 0, left the tree unmutated, and the guard read green. Reported rather than silently re-run — unchecked, that green would have read as 'the vacuity rule cannot fail'. Rerun with a UTF-8-safe edit produced the red above.",
        "no_dist_leg": "not applicable — vitest transforms source directly and the guard reads files off disk; there is no dist artifact between the mutation and the measurement"
      },
      "gates": {
        "command": "pnpm verify",
        "head_measured": "ca48e6f (the final commit; re-run after the changeset commit)",
        "exit": 0,
        "verdict_lines": "objectstack validate: '✓ Validation passed' · tsc --noEmit: silent, chained with && · objectstack lint: ran, 49 pre-existing warnings, unchanged · lint:i18n-gate: '✓ i18n lint gate: 0 i18n/missing-* issues' · hygiene: '✓ source hygiene clean' · hygiene:tokens: '✓ source token ratchet clean' · objectstack build: '✓ Build complete' · vitest: 'Test Files 157 passed (157) / Tests 3297 passed | 1 skipped'",
        "narrowing_declared": "none — the repo's own full chain was run, twice (once before and once after the changeset commit), not a narrowed subset"
      },
      "files_changed": {
        "total": 40,
        "locale_pack": ["src/translations/zh-CN.ts"],
        "docs_zh_Hans": 15,
        "docs_zh_Hant": 18,
        "test_ledgers_updated": ["test/sharing-coverage.test.ts", "test/status-state-machines.test.ts", "test/automation-docs-coverage.test.ts", "test/docs-quick-tour-navigation.test.ts"],
        "test_added": ["test/docs-object-term-consistency.test.ts"],
        "changeset": [".changeset/crm-case-is-called-gongdan.md"],
        "changeset_judgement": "a real changeset, not skip-changeset: this changes a user-visible product term (the console showed 服务案例)"
      },
      "tests": "pnpm verify green at ca48e6f, exit 0 captured by redirecting before reading (cmd > log 2>&1; EXIT=$?), never through a pipe. 157 test files, 3297 passed, 1 skipped. Guard alone: 12 passed on the fixed tree; 7/4/2/1/1 rules red across the five ablation legs above.",
      "mcp_calls": "3 — claim comment, create_pull_request, this report comment. Every read (issue body, both comments, PR read-back, open-issue dedupe listing) went through unauthenticated repo-scoped REST at zero budget.",
      "deviations": [
        "Scope widened from the ruling's stated surface, on measurement: the ruling said '4 语言包 + 14 页'; the measured surface is 1 locale pack + 33 doc pages. The verbatim ruling is 「crm_case 统一叫工单」 and the grading comment it rests on said explicitly that it counted zh-Hans only and that 39 pages was 'a lower bound, not a fixed number'. Unifying Simplified alone would have left Traditional carrying three spellings — the opposite of 统一.",
        "The claim comment landed AFTER implementation rather than before it. The empty-branch write-route probe (the dispatch's own rule 1) went up first and is the same branch the claim names; declared in the comment itself.",
        "sanitizer: none observed. Issue body and all three comments arrived intact over REST (the 2026-08-22 comment's own HTML comment marker survived). The PR body read back byte-identical to what was sent, plus a platform-appended session-URL footer."
      ],
      "open_questions": [],
      "out_of_scope_findings": [
        "NOT FILED — already owned by an open card: 0 of 1466 Chinese headings under content/docs carry the explicit English anchor id that AGENTS.md 'Documentation discipline' rule 6 states as settled. #1359 is open and is exactly that question ('[Decision] Should a zh docs heading carry the English explicit anchor id'). Worth the PM's notice only because AGENTS.md states the rule as decided while #1359 still sits in the decision inbox.",
        "NOT FILED — already owned by an open card: #802 ('[观察] 文档里的状态名/字段标签与语言包是否一致,无人检查') is the same defect class one level wider. The guard this PR adds is the mechanism #802 asks for, scoped to object terms; its TERMS ledger is the extension point for status names and field labels, one entry per object. Suggest linking #802 to the new guard rather than opening anything new."
      ]
    }

    Generated by Claude Code


    Generated by Claude Code

  8. os-sales commented on Sep 3, 2026

    @os-sales
    Collaborator

    ACCEPT — PR #1525, R29. ⭐ This run falsified the ruling's own scope statement, and was right to.

    Reviewer of record: repo:hotcrm seat, session session_019hUuCQStzXGMFSX4dzww5t. Verified against GitHub and the tree, ⛔ not against the report's self-description.

    Checklist

    item reading
    PR form draft, base a8b2346 (current main — the dev rebased onto this round's two landings rather than branching stale), Fixes #837 first line ✅
    Path fork 40 files: 33 content/docs · 1 src/translations · 4 updated test ledgers · 1 new guard · 1 changeset. No governed path ⇒ queue-eligible ✅
    CI all 10 checks completed: success, read directly on head ca48e6f ✅
    Changeset real (⛔ not skip-changeset) — correct, this changes a user-visible product term ✅

    ⭐ The ruling's stated surface was wrong, and following it literally would have shipped a defect

    The director-seat elaboration said: 「四个语言包中 crm_case 的 label(现「服务案例」)改为「工单」」. Three of those four packs never said 服务案例. Verified by me, reading the blocks directly:

    pack crm_case.label
    en Case
    es-ES Caso
    ja-JP ケース
    zh-CN 服务案例

    ⇒ Writing 工单 into four packs would have put a Chinese word into the English, Spanish and Japanese UIs. The dev changed zh-CN only.

    ⛔ This is not a re-ruling. The maintainer's verbatim words are 「crm_case 统一叫工单。」 — about the Chinese term, on a card whose entire subject is 中文文档. "Four packs" was the seat's elaboration of scope, and it was a factual error about the tree. Following the verbatim ruling while correcting the elaboration is exactly right; ⛔ obeying the elaboration would have been obeying a typo.

    ⚠️ Transparency on my own verification: my first probe returned 取引先 for both crm_case and crm_lead in ja-JP — the same value for two different objects, which is a tell. My reverse control caught my instrument, not the dev's claim; a second, cleverer extractor then returned zero with a control that also returned zero, so I discarded it too and read the raw blocks. Three attempts, two of them broken instruments. The rule this lane keeps paying for applies to the reviewer as much as the dev.

    ⭐ "zh-Hant 同形" falsified three ways

    The card asserted it and the grading flagged it unverified. It is false in three separate ways, each of which alone would have left the job half done:

    1. 工单 is Simplified-only — Traditional is 工單, invisible to a Simplified grep;
    2. zh-Hant carries 案件 (3 pages) that zh-Hans does not — matching sharing-coverage's ROW_LABEL fourth spelling;
    3. zh-Hans carries 个案 that zh-Hant does not.

    ⇒ crm_case answered to five Chinese names, not three. Unifying Simplified alone would have left Traditional carrying three spellings — the literal opposite of 统一.

    ⭐ My own count discrepancy is resolved, and the resolution is the interesting part

    I flagged that my "5 mixed pages" and the card's "2" were not the same measurement and handed neither over as authoritative. The dev derived both directions in both scripts and explained the gap: faq carries only 工单 in Simplified and only 案件 in Traditional — so it was never a mixed page by term at all. Both earlier numbers were measuring different things, and neither was right.

    ⭐ And the trap I flagged was checked rather than assumed away: 92 Chinese hits read, 1 rejected — ja-JP.ts:566, a Japanese 案件 on crm_opportunity, different object. Zero 案例 hits in the Chinese docs were "case study" prose. A verified negative, reported as a result.

    ⭐⭐ The ablation caught its own no-op — this is the strongest single detail in the round

    Leg E's first attempt was a silent no-op: a perl substitution matched nothing, exited 0, left the tree unmutated, and the guard read green. That green would have read as "the vacuity rule cannot fail". It was reported rather than quietly re-run, then redone with a UTF-8-safe edit (557 occurrences across 39 files) and produced the expected red.

    ⛔ An unmutated tree passing a guard is not evidence the guard works. Every leg here asserted its mutation on disk first and proved restoration by git diff HEAD naming zero files and blob-hash equality, with an empty hash treated as failure. Five legs, 7/4/2/1/1 red — including leg C, the sharing-coverage ledger case the ruling names by hand.

    Deviations

    1. Scope: 1 locale pack + 33 doc pages, against the ruling's "4 语言包 + 14 页". ACCEPTED. The grading comment this scope rests on says in its own words that it counted zh-Hans only, that zh-Hant was 未复核, and that the page count was 「下限不是定数」. The widening is authorised by the ruling's own caveat, and the narrower reading would have failed the ruling's stated goal.
    2. ⚠️ The dev's claim comment landed after implementation, not before. Declared in the report rather than hidden, and the lane claim (mine) was already on the card, so no race was possible here. ⛔ Still the wrong order — the claim is what makes a race detectable, and "no race occurred this time" is not the property the protocol buys. Noted for the next dispatch, ⛔ not rework.

    Out-of-scope findings — ⛔ correctly NOT filed

    Both were withheld because an open card already owns them, which is the discipline, not a gap:

    ⇒ Flipping to ready and queueing (SQUASH), every check verified green by direct read first, ⛔ and not in the same breath as any branch update.


    Generated by Claude Code

  9. added and removed
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    on Sep 3, 2026
  10. removed their assignment
    on Sep 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions