# State Schema: `state/customers/CUST-*.json` Owned by: `claw-customer-radar` (write); read by `claw-report-writer`. ## Identifier convention `CUST-` where slug is derived from the primary email domain or the boss-confirmed customer name. Examples: `CUST-clientco`, `CUST-customer-A`, `CUST-momo-tech` Unconfirmed candidate customers are stored as `UNCLASSIFIED-.json` and surface in W1 audit for boss to confirm/merge. ## Top-level shape ```jsonc { "id": "CUST-clientco", // string "name": "客户A", // string, boss-confirmed display name "domains": ["clientco.com", "client-co.cn"], // array, all email domains for this customer "lifecycle_stage": "At-Risk", // enum: Lead | Onboarding | Active | At-Risk | Churned "关联项目": [ // array { "id": "PRJ-2025-001-客户A官网改版", "状态": "Stalled", "金额": 120000 }, { "id": "PRJ-2026-014-customer-A-supply", "状态": "Active", "金额": 80000 } ], "健康度": { // object — Customer Health Score "score": 62, // int 0..100 "档位": "黄(注意)", // enum: 绿(健康) | 黄(注意) | 红(危急) "构成": { // object, weighted components "项目健康": 0.60, // float, weight 0.4 "情绪趋势_30d": -0.5, // float, weight 0.3 "邮件频率_normalcy": 1.0, // float, weight 0.2 "金额规模_normalized": 0.7 // float, weight 0.1 }, "computed_at": "2026-05-09T07:30:00Z", "previous_tier": "绿(健康)", // for Andon detection "tier_change_at": "2026-05-08T07:30:00Z" }, "情绪趋势_90d": [ // array, monthly buckets {"month": "2026-02", "score": 0.7, "代表词": "期待 / 配合", "n_quotes": 12}, {"month": "2026-03", "score": 0.5, "代表词": "等候 / 询问", "n_quotes": 9}, {"month": "2026-04", "score": 0.2, "代表词": "催 / 失望", "n_quotes": 14} ], "RFM": { // object "Recency": "5d", // string, time since last interaction "Recency_iso": "2026-05-04T16:32:00Z", // ISO timestamp for sorting "Frequency_30d": 14, // int, message count last 30d "Frequency_90d_baseline": 38, // int, baseline for normalcy comparison "Monetary": 200000 // number, sum of associated project amounts }, "我方接口人": [ // array, internal people who touch this customer {"name": "张三", "角色": "项目经理", "可靠度": "中", "primary_for_projects": ["PRJ-2025-001"]}, {"name": "李四", "角色": "客户总监", "可靠度": "高", "primary_for_projects": ["PRJ-2026-014"]} ], "客户方决策链": [ // array, external people on customer side { "name": "客户A-王总", "email": "wang@clientco.com", "角色": "决策者", // enum: 决策者 | 执行接口 | 用户 | 阻拦者 "态度": "中性偏冷", "last_intervention": "2026-04-22" }, { "name": "客户A-赵经理", "email": "zhao@clientco.com", "角色": "执行接口", "态度": "积极", "last_intervention": "2026-05-04" } ], "VoC_关键原话_30d": [ // array, max 5 { "date": "2026-04-22", "speaker": "王总", "speaker_email": "wang@clientco.com", "原文": "再这样我就找别家了", "性质": "强负向", // enum: 强正向 | 正向 | 中性 | 负向 | 强负向 "source_email_id": "msg-xyz@..." }, { "date": "2026-04-15", "speaker": "赵经理", "speaker_email": "zhao@clientco.com", "原文": "你们做事还是认真的", "性质": "正向", "source_email_id": "msg-uvw@..." } ], "流失预警信号": [ // array { "信号": "决策者亲自施压", "强度": "高", // enum: 高 | 中 | 低 "时间": "2026-04-22", "rule_ref": "R-04", "source_email_ids": ["msg-xyz@..."] }, { "信号": "PRJ-001 stall 11 天", "强度": "中", "时间": "2026-05-09", "rule_ref": "R-01" } ], "老板视角建议": "本周内安排老板亲自给王总通一次电话——执行层已经救不回来了", "_meta": { "created_at": "2025-06-01T09:00:00Z", "updated_at": "2026-05-09T07:30:12Z", "atlas_version": "v0.4", "last_andon_alert_at": "2026-05-08T07:30:00Z", "last_andon_acknowledged_at": null } } ``` ## Customer Health Score (CHS) formula ``` CHS = 100 × ( 0.4 × project_health + 0.3 × normalize(sentiment_trend_30d, [-1,1] → [0,1]) + 0.2 × email_frequency_normalcy + 0.1 × monetary_scale ) tier: CHS >= 80 → 绿(健康) 60 <= CHS < 80 → 黄(注意) CHS < 60 → 红(危急) ``` Score is clamped to [0, 100] and rounded to nearest integer. ## Andon Alert triggers Fire to `state/runs/alerts.json` (and email + WeChat if configured) when: 1. **Tier downgrade** — `健康度.档位` drops from 绿→黄 or 黄→红 2. **Strong-negative VoC** — new entry in `VoC_关键原话_30d` with `性质 == "强负向"` 3. **Multi-signal churn** — ≥ 2 active `流失预警信号` simultaneously Suppression: same `(customer_id, trigger_type)` does not re-fire within 24h of `last_andon_acknowledged_at`. ## Cross-customer data isolation When `claw-report-writer` renders a customer-specific report: - Read ONLY this customer's JSON file - Read ONLY projects whose `客户方` matches this customer's `id` - Read ONLY people who appear in this customer's `我方接口人` - Never include comparison metrics ("compared to other customers") This isolation is enforced at the file-read layer, not just at template-render time. ## Boss override format ``` OVERRIDE CUST CUST-clientco lifecycle_stage="Active" OVERRIDE CUST CUST-clientco merge_into="CUST-clientco-group" OVERRIDE CUST UNCLASSIFIED-newdomain.com promote_as="CUST-newprospect" ``` ## Display rules for `claw-report-writer` - Daily Brief: only customers with tier change or new churn signal in last 24h - Weekly Rollup: top 5 customers by CHS movement (up or down) - Monthly Health Report: one full page per Active or At-Risk customer - Andon Alerts: red customers with new strong-negative VoC, real-time ## Privacy - Right-to-delete: `atlas-customer-purge --id CUST-clientco` wipes all related project state, removes person↔customer associations, writes tombstone - Audit log preserved (anonymized) for compliance