# Handoff · customer-contact-graph 票 00 原型 demo(2026-09-09 会话) 接手人:新 agent。本会话主线一句话:**用户改道「先原型后 spec」——票 00 原型 demo 写到一半(CSS 完整、body/JS 未落盘),续写、自检、交验收。** ## 1. 你接手的是什么 - Effort:customer-contact-graph(客户联系人图谱 + A4-4-2 §6 整节落地)。改序后的地图与票链见 `.scratch/customer-contact-graph/map.md`(本会话已更新:新票 00 插最前、frontier=00、Decisions so far 有改序决策原文)。 - 本会话前半:票 01 spec 冻结 grilling 刚开始(Q1 边模型已 pitch、**未拍板**),用户 `/wait-what` 改道:先做纯前端 mock 原型 demo 验证需求理解,理解确认后再回 spec 冻结。 - 用户两拍板(AskUserQuestion,2026-09-09): 1. demo 范围 = **图谱全量 + 列表侧**(u536 图谱 15 条 + u533 列表侧关键交互); 2. 原型验收通过后**升格为正式 demo**(票 06 改写为「接真实后端 API」不重写)。 - 票务簿记**已完成**(无需重做):`map.md` 改序 + 决策;`issues/00-prototype-demo.md` 新建(in_progress,deliverables 全量在此,**先读它**);`issues/01-spec-freeze.md` 加 blocked by 00;`issues/06-demo-extension.md` 已改写为「接真实 API」版。 ## 2. 当前进度(票 00 文件状态) 目标文件:`.scratch/customer-contact-graph/prototype/prototype-contact-graph.html` - **已落盘并验证**:第 1 块 = DOCTYPE + `` + 全部 CSS,**185 行,止于 ``**(含全部类名:`.gcard/.dot/.edge-*/.pill/.sp-*/.modal/#toast` 等)。 - **未落盘**:第 2 块(body 骨架)、第 3/4 块(JS)。第 2 块 body 内容本会话已定稿但两次 Write 均因工具载荷丢失(file_path 参数被吞 → 40400)未写入;重试被用户取消以发起本 handoff。**body/JS 直接按本文档 §3 设计重写即可,不要试图找回旧消息**。 ## 3. demo 设计定稿(会话内已拍板,重建时不再重新设计) - **总体**:单文件 HTML,零后端依赖;右侧固定 352px「规则/状态/日志」三 tab 面板(surface the state,票 01 grilling 的活素材);主体 = 列表/图谱 pill 切换。 - **mock 数据(9 联系人,图谱侧权威)**: - 王强/董事长/L1/13123481545;陈大壮/股东/L1/13897283272/isKey=1/isInternal=1;张明/股东/L1/13897281545 - 胡子/采购经理/L2/13897283272/isInternal=1/来源转介绍;张华/市场经理/L2/13812383272;卢伟/市场经理/L2/13897283272/来源组会 - 邓伦/采购专员/L3/13127283272;王大牛/市场专员/L3/13897283272;江小白/其他(类别=投标商务)/L=null/13897283272/来源组局 → 未识别层级区 - **职务字典**:高层管理(董事长=1/股东=1)、采购(经理=2/专员=3)、市场(经理=2/专员=3)、人事(经理=2)、投标商务(其他=null)、自定义职务(无层级)。来源字典:转介绍/组会/组局/其他。 - **布局**:tier 行制——L1 顶部、公司节点(广州宏远智能电子科技有限公司,深蓝卡)水平居中**固定不可拖/无圆点**、L2/L3 依次向下、未识别层级 = dashed zone(底部右侧)。层级越高越靠上、同级并列、多顶层并列。 - **自动边规则(demo 候选规则,规则面板明示,票 01 定稿)**:L 层的上级 = 同职务类别 L-1 的全部联系人(多父);类别内无 L-1 → 全部 L-1;无 L-1 → 不连线。蓝色实线。 - **边模型(demo mock 了票 01 Q1 推荐案语义)**:`edges[]{id,parent,child,source:auto|manual}` + `vetoed[]`(系统边删除**并保存** → 永久否决,重建跳过;手动重连同对不受限)+ `pendingAdds/pendingDeletes`;保存时 DFS 环检测 → 阻止保存 + 高亮环边;自连/重复/连公司节点 → Toast 拒绝(重复文案=「已存在连线」)。 - **编辑态全套**:绘制自定义关系→按钮变橙「完成绘制」+顶部提示条+卡片顶/底蓝圆点;拖卡调位;从底圆点拖出连线(SVG 预览线跟随,落上级底→下级顶);悬停边变红虚线+中点 × 删除(auto 边二次确认对话框);保存(提交 pending + 记「关系变更日志」)/ 重置(有未保存时二次确认,恢复进编辑时快照)/ 完成绘制(≠保存;有未保存 → 三选弹窗:继续编辑/放弃修改/保存修改)/ 切视图未保存拦截。 - **脱敏(拍板口径,与原型分歧已在页脚+规则面板显性标注)**:列表单元格 + 图谱卡片电话均默认脱敏 `138****3272`,点击 → 明文 + 埋点日志(谁/何时/哪个联系人号码);「显示完整号码」批量揭当前页;批量导出 CSV 恒脱敏。 - **6.2 快速添加**:modal 多行(姓名*、职务*=类别→职务二级联动+自定义、电话选填、关键、内线、来源);空行不提交;查重(手机号 / 客户+姓名+职务)→ 行内疑似提示 + 该行「确认知悉」勾选后方可提交;错误保留全部内容并定位首错行;成功关 modal + 刷新列表 + 图谱(新联系人进 tier、自动边重建)。 - **6.3 页内编辑**:行内编辑 + 修改行高亮 + 行级「撤销修改」;保存全部(校验全部修改行、首错行定位、成功后职务变更触发图谱自动边重建);重置(确认);退出编辑/切图谱未保存拦截(三选)。 - Toast/对话框自实现(不用 alert/confirm)。分页 10条/页(9 联系人 1 页,pager 展示即可)。 - **写法约束**:body/JS 的 DOM 类名必须对上已落盘 CSS(见 §2 文件)。 ## 4. 已知坑(本会话实录,均已有记忆/AGENTS.md 条目) - Write 大 CJK 载荷非确定性丢参(file_path 被吞 → 40400):串行单发重试一次;再败 → 载荷写 workspace 文件 + python 复制(AGENTS.md 降级链)。首报 "unknown" 多为误报——以 Test-Path/回读为准。 - PowerShell 无 `&&`,用分号。Glob 对 `.scratch/` 隐藏目录返回 0 结果(用 Bash ls / 直接 Read)。 - 大文件分块写(≤1000 行/次)防流断(AGENTS.md「Stream dropouts」节)。 ## 5. 下一步(按序) 1. 续写 prototype HTML:第 2 块 body 骨架 → 第 3 块 JS(mock 数据/state/布局/自动边/渲染)→ 第 4 块 JS(图谱交互/保存重置/列表侧)。每块落盘后回读验证。 2. Playwright 自检(chromium 已缓存于 `.scratch/customer-e2e/.venv10`;file:// 直开即可):无 JS 报错 + 截图核对布局/卡片/边。 3. 交用户验收:图谱 15 条逐条过 + 脱敏分歧点 + 规则面板的自动边规则。 4. 通过 → 关票 00,重启票 01 grilling(10 组逐组定稿,Q1 起点见 §6;demo mock 实现 = 对照物)。偏差 → 只改原型文件。 ## 6. 票 01 Q1 推荐案(已 pitch 未拍板——重启 grilling 的起点,勿当已定稿) 单表 `customer_contact_edge`(继承 BaseEntity):customer_id / parent_contact_id / child_contact_id / `edge_source`(auto|manual) / `edge_status`(active|vetoed);系统边删除 = status→vetoed(行保留、deleted=0,防 @TableLogic 吞掉否决记录),manual 边删除 = 常规逻辑删;vetoed 永久(职务变不恢复)+ 手动重连例外;唯一性 = 服务层事务内校验 + `uk(customer_id,parent,child,edge_source)` 兜底;新错误码 67016 CODE_EDGE_INVALID(自连/重复/端点无效)+ 67017 CODE_EDGE_CYCLE(保存时环检测);关系变更日志 = customer_oplog 新 action `GRAPH_SAVE`。事实底盘(CustomerContact 四列留位、67xxx 用到 67015、CustomerViewLog 审计分离先例、DictItem parentId 两级树、ContactDTO phone 现无脱敏等)已在会话中核实,代码即证据。 ## Suggested skills - `prototype` — 票 00 方法论(已加载过;UI.md 3-变体默认不适用——设计权威=Lanhu 原型,唯一自由度做成规则面板供挑战)。 - `webapp-testing` — 收尾 Playwright 自检截图。 - `grilling` + `domain-modeling` — 票 01 重启(demo 验收通过后,一次一问)。 - `ask-matt` — 路由不确定时。 ## 权威文件索引 - 地图(已改序):`.scratch/customer-contact-graph/map.md` - 票 00(deliverables 全量,先读):`.scratch/customer-contact-graph/issues/00-prototype-demo.md` - 需求权威:`.scratch/opportunity-bugfix/lanhu-pages/A4-4-2_客户详情(联系人).md`(u533/u536 在行 277-279) - 原型半成品:`.scratch/customer-contact-graph/prototype/prototype-contact-graph.html`(185 行,head+CSS 完整) - 编码/工具纪律:`AGENTS.md`