Repository navigation
Track A · crm_contact: the buying-centre map (role, attitude, relationship strength) — REQ-0004 #1913
Description
Activity
- addedenhancementNew feature or requestNew feature or requestpm:epicParent delegated to a dedicated epic PM — other PMs never dispatch into its subtreeParent delegated to a dedicated epic PM — other PMs never dispatch into its subtreepm:dispatchedDispatched to a dev agent by /pm-dispatchDispatched to a dev agent by /pm-dispatch
on Sep 15, 2026 os-elon-musk commented
on Sep 15, 2026 CollaboratorAuthorMore actionsClaim: PM loop round 6
Session:session_01T3YsvpK1PvYf9n1YUhYP6W
Branch:claude/issue-1913-contact-buying-centre-map
Worktree:hotcrm-issue-1913
Domain: single-lane repo (hotcrm) — nodomain:*
File surface:src/sales/objects/contact.object.ts,src/sales/translations/{en,zh-CN,es-ES,ja-JP}.ts, one page undercontent/docs/(stop on breach; explain in the report)
Container & model:M,mode:subagent,model: 默认判断档
Clause-②: no
Thread-read: none (card opened by this seat at 2026-09-15T14:5xZ, no comments yet)
Serial constraints cleared: NOT cleared — deliberately serialised. Track A 的四张实现卡(#1913 #1914 #1915 #1916)文件面相交:都动src/sales/objects/与同四个语言包。⇒ 硬串行,本卡是链首,#1914 / #1915 / #1916 依次在前一张 MERGED 后才派。同仓在飞:#1907 是pm:blocked的打包卡,其分支claude/issue-1907-sales-app-service-module移动整个src/,但它在等上游发版、未在飞,⛔ 本卡落地后它重启时须先合 main。授权
维护者 2026-09-15:「开始 Track A」(方向性授权,覆盖整条执行链)。
派发排序与理由
四张按复杂度递增:contact(纯字段)→ lead(字段 + 审批)→ account(能力闸 + 审批)→ opportunity(多字段组 + 状态变更审批)。链首取最小的一张,先把「加字段 → 四语言包 → 用户文档 → changeset」这条链路验通。
Generated by Claude Code
os-elon-musk commented
on Sep 15, 2026 CollaboratorAuthorMore actionsos-dev-report
{
"issue": 1913,
"status": "done",
"branch": "claude/issue-1913-contact-buying-centre-map",
"pr": "#1917",
"premise_still_valid": true,
"summary": "REQ-0004 implemented on crm_contact only. Newbuying_centrefieldGroup holding three optional, ordered/generic selects — buying_function (6 values), attitude (5, champion->blocker), relationship_strength (5, distant->trusted_advisor) — plus a group-REFERENCE form section ({ group: 'buying_centre' }, AGENTS.md ladder rung 2, no per-field enumeration anywhere) and the three as columns on all_contacts, which already groups by account. All four locale packs in the same commit (en/zh-CN/es-ES/ja-JP: 3 field labels + 16 option labels + the _sections row each). New user page content/docs/sales/buying-centre.{mdx,zh-Hans,zh-Hant} registered in all three meta.json faces and linked from the sales index and contacts page; contacts.* section table 6->7 rows in all three locales. One changeset, 'hotcrm': minor (additive user-visible metadata, no breaking change) naming REQ-0004. No hook, no flow, no validation, no default, nothing required — proven by grep, seetests. TWO DECISIONS MADE AND DECLARED: (1) buying_function is SINGLE-valued, argued in the PR body from GroupingConfigSchema/GroupingFieldSchema in the installed @objectstack/spec/ui — grouping is one server-side aggregate whose header 'carries its raw stored value', so a multi-valued column groups by the COMBINATION and loses REQ-0004 acceptance 2. (2) The option sets are authored INLINE on the object, not in src/sales/objects/_picklists.ts: REQ-0004 names that file parenthetically as 'where the canonical sets already live', and that file's own header admits only vocabularies carried by MORE THAN ONE declaration; these three are crm_contact-only, exactly likedepartment, which is inline. Flagging it because it is the one place this PR reads the record's Product response narrowly. PREMISE NUANCE (premise still valid): REQ-0004 calls the first attribute 角色 / 'role', and the platform refuses that word — security-role-word (ADR-0090 D3) rejected bothbuying_roleas a field NAME and 'Buying Role' as a LABEL, two successivepnpm validateexit-1 runs quoted in the PR body. Name and English label take the remedy the rule itself prescribes (function); the rule matches /\brole(s)?\b/i on the English declaration surface and does not read locale packs, so zh-CN 采购角色 / es-ES Rol de Compra / ja-JP 購買役割 keep the natural term, each with a note beside it. SCOPE: crm_contact only — no lead/account/opportunity, nothing under src/service, src/revenue, src/marketing, nothing under content/docs/releases. docs/STATUS.md was edited because test/docs-declared-versions.test.ts went red on it (seetests). ASSIGNEE: the field was already set to os-elon-musk on arrival; ⛔ not written by me.",
"tests": "pnpm verify— the full chain package.json declares (validate && typecheck && lint && lint:i18n-gate && hygiene && hygiene:tokens && build && test), ⛔ not narrowed. Exit code written to a file BEFORE any pipe:pnpm verify > verify2.log 2>&1; echo \"VERIFY_EXIT=$?\" >> verify2.log-> VERIFY_EXIT=0. Run under the container's shared heavy-verify lock (scripts/pm/os-verify-lock.sh, OS_VERIFY_LOCK_SLOT=hotcrm-1913): 'VERDICT batch-last-exit 0 ... held the lock 167s (2m47s) waited 0s'. Per gate, from that run: validate '✓ Validation passed' (0 errors; the remaining warnings are pre-existing carrier-only fields on crm_article_feedback/crm_forecast/crm_quote_line_item — none names crm_contact); typecheck exit 0; lint exit 0; lint:i18n-gate '✓ i18n lint gate: 0i18n/missing-*issues'; hygiene '✓ source hygiene clean'; hygiene:tokens '✓ source token ratchet clean' — business semantics ~50,338/53,000, interaction layer ~29,397/31,000, authored total ~92,324/97,000, no ceiling moved; build exit 0; test 'Test Files 166 passed (166) / Tests 3479 passed | 1 skipped (3480)'. RED->GREEN on the way: the first full run failed one test — test/docs-declared-versions.test.ts, 'Fields: page says 331, the stack registers 334'. Fixed at the source of the drift by re-transcribing docs/STATUS.md'spnpm validatesummary from this branch's own run (331 -> 334 fields), not by touching the guard. ARTIFACT READ-BACK (dist/objectstack.json, not an assertion about the source): fieldGroups keys 'identity account_info buying_centre contact_info mailing_address additional preferences'; buying_function=select/6opts/required=false/multiple=false, attitude=select/5opts/required=false/multiple=false, relationship_strength=select/5opts/required=false/multiple=false; form sections ["identity","group:buying_centre","contact_details","mailing_address","comm_preferences"] — the buying-centre section carriesgroupand NOfields; grid columns include buying_function, attitude, relationship_strength; and all four packs resolve section + 3 labels + 6/5/5 option labels (en 'Buying Centre', zh-CN 采购决策圈, ja-JP バイイングセンター, es-ES 'Centro de Compra'). ACCEPTANCE 4 PROBE (no write is refused):grep -rn 'buying_function|relationship_strength|\\battitude\\b' src/ --include=*.tsminus the object, the view and the four packs returns NOTHING — no hook, no flow, no validation, no action, no skill reads them; combined with required=false and no option default, a contact with all three blank saves as before. NOT MEASURED: the browser. Nothing here is a renderer change, and no ablation was authored — this card adds no gate, so there is no new guard whose failure needed proving.",
"mcp_calls": "0 — no MCP GitHub tool was called at any point, read or write. Every GitHub touch went through the REST proxy with curl + $GITHUB_TOKEN.",
"api_writes": "5 writes, all of them inside the dispatched budget's three kinds:git pushx3 (1: empty branch as the mandated write-route probe; 2: the implementation commit 9212d29; 3: the docs/STATUS.md commit 230b11c) — final head 230b11c confirmed present on the remote withgit ls-remoteBEFORE this report; POST /repos/objectstack-ai/hotcrm/pulls x1 (draft PR #1917, base main, draft=true confirmed on read-back); POST /repos//issues/1913/comments x1 (this report). ⛔ No labels touched, ⛔ no PATCH of any body, ⛔ no ready flip, ⛔ no enqueue, ⛔ no merge. Reads (not writes): GET issues/1913, GET pulls/1917 for the full body read-back — the stored PR body came back byte-identical to what was sent, footer included.",
"open_questions": [],
"out_of_scope_findings": [
"to file (class a — a documented instruction that fails when followed; dedupe words: src/flows path, ADR-0130 package move, contact-welcome.flow.ts, docs stale directory, content/docs admin tip): the product docs still tell readers to edit files undersrc/flows/, a directory that stopped existing in the ADR-0130 layout move (#1910). Named sites: content/docs/sales/contacts.mdx:95 and .zh-Hans.mdx:95 (src/flows/contact-welcome.flow.ts, really src/sales/flows/), sales/quotes.mdx:155 and :161, revenue/approvals.{mdx,zh-Hans,zh-Hant}:~140, revenue/billing-handoff.{mdx,zh-Hans,zh-Hant}:~136. An admin following the instruction finds nothing there. Not fixed here: different defect class from this card and it spans three packages' docs across three locale faces.",
"to file (class a — a doc states machine facts that are false; dedupe words: crm_contact.department picklist, contacts.mdx department list, docs hand-copied roster, IT Legal Other department, AGENTS.md documentation rule 5): content/docs/sales/contacts.mdx:94 (and the two zh faces) says the department list is 'Sales, Marketing, Engineering, Finance, IT, Operations, HR, Legal, Executive, Other'. crm_contact.department declares executive, sales, marketing, engineering, support, finance, hr, operations — so IT, Legal and Other are named and do not exist, and Support is missing. Exactly the hand-copied machine roster AGENTS.md documentation rule 5 forbids, already drifted. Untouched by this PR.",
"noted, not filed: the 'No org chart — and what to use instead' section of contacts.mdx now reads thin — it answers 'who matters here' with job title + department, and the buying centre is the better answer as of this PR. The section is not FALSE, so it is a wording call, not a defect. Carrier named: whoever next edits contacts.mdx, most likely the REQ-0002 Track B work on deal-side stakeholder coverage; the new buying-centre page already carries the better framing and is linked from that page's section table."
]
}
Generated by Claude Code
os-elon-musk commented
on Sep 15, 2026 CollaboratorAuthorMore actions复核裁定:ACCEPT
卡 #1913 · PR #1917 · 头
230b11cb68· 底main
一、边界
查项 结论 销售对象 只动 src/sales/objects/contact.object.ts,其余销售对象零改动 ✅禁区 src/service/、src/revenue/、src/marketing/零命中 ✅ 禁区 content/docs/releases/零命中 ✅ 四个语言包 四个都在同一笔里 ✅ hook / flow / validation 零新增 —— 记录明写「由人和报表读,不由机器判」,守住了 ✅ C(客户定制层)项 零实现 ✅ 二、两处看似超范围,实为规格内
复核时我先把这两处当超范围挂起,逐条回查规格后撤销:
-
视图文件改动 —— 不是顺手改。
docs/requirements/0004-contact-buying-centre-map.md的 Acceptance 第 2 条要求「三个字段在联系人列表视图里可筛可分组」。不动视图这条验收就不成立。在范围内。 -
三语言文档 —— 不是自行扩面。
content/docs/sales/本来就是.mdx/.zh-Hans.mdx/.zh-Hant.mdx三份加三个meta*.json的固有形状,只补一份反而破了目录的既有约定。是本仓的既定形,不是新开面。
三、
docs/STATUS.md的改动test/docs-declared-versions.test.ts因字段数 331 → 334 变红。开发没有去动那道守卫,而是在源头重抄了自己分支的 validate 摘要 —— 方向正确。守卫仍然在守,读数跟着事实走。✅四、role 词禁令的前提冲突 —— 我独立复验过
卡面规格写的是「采购角色(buying role)」,但平台 lint 会拒。我没有采信 PR 正文的说法,自己读了
node_modules/@objectstack/lint/dist/index.js:function identifierHasRoleToken(name) { ... } function labelHasRoleWord(label2) { if (typeof label2 !== 'string') return false; return /\brole(s)?\b/i.test(label2); }
规则只测英文声明面(标识符 + 英文 label),⛔ 不读语言包。
开发的化解办法成立:字段名取
buying_function、英文 label 取Buying Function,四个语言包里保留读者真正在说的词(采购角色 / 購買役割 / Rol de Compra),每处都带一条本地语言的注释说明为什么英文面用了另一个词。这是在规则的真实边界内保住了业务语义,不是绕过规则。 ACCEPT。五、形式检查
- 正文首行
Fixes #1913✅ - 全文恰好一个关闭关键词 ✅
- draft ✅ / base
main✅ /mergeable_state: clean✅ - CI 10/10 success,0 红 0 挂起 ✅
- changeset:
'hotcrm': minor✅ —— 有用户可见元数据变化,不是 docs-only,级别对
六、复核中浮出的三条发现(不阻断本卡,PM 另立卡)
开发按 (a)(c) 分类报了两条,我自己又量出第三条:
- (a) 文档仍在教读者去改
src/flows/—— 该目录在 ADR-0130 的搬迁里已移除。已点名的站点:content/docs/sales/contacts.mdx:95与.zh-Hans.mdx:95、sales/quotes.mdx:155,161、revenue/approvals.*:~140、revenue/billing-handoff.*:~136。 - (a)
crm_contact.department花名册失真 ——contacts.mdx:94列的 IT / Legal / Other 三项在声明里不存在,而实际存在的 Support 没列。 - (c) 元数据陷阱(我量的) —— 同一文件 第 19 行的 fieldGroup label
'Account & Role'带着 role 词却能过,因为labelHasRoleWord只测字段 label,⛔ 不测字段组 label。同一个词在同一个文件里一处被拒一处放行,这是规则面本身的洞,不是本 PR 的债。
这三条都不在本卡的文件面里,⛔ 不塞进本 PR 扩面。PM 立卡走各自的队列。
裁定:ACCEPT。 PR #1917 转 ready,走普通队列 squash 落地。
Generated by Claude Code
-
- removedpm:dispatchedDispatched to a dev agent by /pm-dispatchDispatched to a dev agent by /pm-dispatch
on Sep 15, 2026
Part of #1904 — Track A 实现批,REQ-0002 前 14 步的销售侧增强。⛔ 不依赖上游发版,与 #1907 的拆包线并行。
权威规格
docs/requirements/0004-contact-buying-centre-map.md(已在origin/main,PR #1912 合入6ff28e7)。其中 C(客户定制层) 项一律 ⛔ 不做 —— 它们不属于标准产品,记录里逐条写明了理由。
串行约束⚠️
Track A 的四张实现卡文件面相交:都要动
src/sales/objects/与四个语言包src/sales/translations/{en,zh-CN,es-ES,ja-JP}.ts。⇒ 硬串行,一次只有一张在飞。 本卡落地后 PM 才派下一张。⛔ 不要并行开工、⛔ 不要顺手改别的对象。
文件面
src/sales/objects/contact.object.ts—— 三个 select,通用选项集,各自成组src/sales/translations/{en,zh-CN,es-ES,ja-JP}.ts—— 四个语言包同笔content/docs/—— 一页面向销售用户解释 buying-centre 概念的用户文档⛔ 不碰其它销售对象、⛔ 不碰
src/service/、src/revenue/、src/marketing/、⛔ 不碰content/docs/releases/。验收
以
docs/requirements/0004-contact-buying-centre-map.md的 Acceptance 节为准,⛔ 本卡不另立标准。另加两条通用项:pnpm verify绿(package.json是权威,⛔ 不自行窄化)。退出码在任何管道之前写进文件再读。为什么先派这张
四张里最小的一张:纯字段 + 翻译 + 文档,无流程无校验。先用它把「加字段 → 四语言包 → 用户文档 → changeset」这条链路验通,后面三张按同一形状推进。
Generated by Claude Code