Repository navigation
fix(leads): 一个 hot 定义 —— Hot Leads 视图阈值对齐 lead_assignment 路由流(rating ≥ 4) - #774
Merged
Merged
Conversation
… routing flow (#766) The `hot_leads` list view cut at `rating >= 4.5` while the `lead_assignment` flow's hot branch (edge `e2`) cuts at `>= 4`. `rating` is a whole-star field — `lead.hook.ts` rounds the computed score, `Field.rating(5)` renders as a star widget with no half steps, and the one seeded 4.5 was deleted for the same reason (#591) — so `>= 4.5` was `== 5` on every row that can exist. A 4-star lead was therefore routed hot, stamped with the 1-day SLA, and its owner alerted "Hot lead — assign within 24h", while the queue that alert points at could never contain it. The flow is the semantics producer (its verdict has user-visible consequences), so the view follows it: threshold `>= 4`. The comment crediting an `auto_flag_hot_lead` workflow is removed — no such automation exists in this repo — and replaced with a reference to the real producer. `test/hot-lead-threshold-parity.test.ts` derives both thresholds from the shipped metadata and pins their agreement, including a truth table that asks the real AutomationEngine and the real view filter, for every rating a lead can carry, whether "routed hot" and "in the queue" give the same answer. Docs: the Hot Leads / High Priority rows on leads.mdx and its zh-Hans / zh-Hant siblings described the pre-fix behaviour (#768 wrote them against the old threshold) and now match what ships. Fixes #766 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VHrPAGEgFDoHjphqYG4BMa
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
CodeQL flagged `highPriorityView` as unused, and it was: the file compares the hot_leads threshold against the lead_assignment hot branch, and high_priority carries no part of that invariant. Removed rather than given an assertion to justify it. Post-#766 high_priority holds the same filter as hot_leads, so the only assertion available would pin that sameness — a guard over an open product decision (#775: merge the two views, or re-differentiate high_priority on a non-rating criterion), either outcome of which would turn it red with nothing broken. A comment now records why the binding is absent, so the next reader does not re-add it. No assertion changed: 16 tests before and after. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VHrPAGEgFDoHjphqYG4BMa
yinlianghui
marked this pull request as ready for review
August 5, 2026 18:51
This was referenced Aug 5, 2026
Closed
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 #766
按分诊裁定执行:仓库里只留一个 hot 定义,语义生产者是
lead_assignment路由流(它的判定有用户可见后果:1 天 SLA + 「24 小时内认领」告警),hot_leads视图跟随它。复核结论:issue 的四条前提全部成立(基线 origin/main = 440db01)
src/views/lead.view.ts:358——{ field: 'rating', operator: 'greater_than_or_equal', value: 4.5 }src/flows/lead-assignment.flow.ts:127边e2——has(record.rating) && record.rating != null && record.rating >= 4,命中后sla_hot(TODAY() + 1)+notify_hot(inbox + email,标题「Hot lead — assign within 24h」)src/objects/lead.hook.ts:146-148——Math.round(clamped),注释「round to WHOLE stars」;字段是Field.rating(5)(lead.object.ts:200),控件只给整星auto_flag_hot_lead是幽灵没有找到任何支持 4.5 的书面理由,反而找到一条相反的记录:
src/data/sales.seed.ts:473-478明确写着「a seeded 4.5 was the one rating in the system the contract could never produce, and it rendered as the broken half-star it was meant to prevent (#591)」—— 半星值在种子数据里已经因为同一个原因被删过一次。裁定的前提成立,按 A 方向实施。改了什么
src/views/lead.view.ts:hot_leads过滤阈值4.5→4;幽灵 workflow 注释删除,换成真实生产者(lead_assignment流的check_hot决策 / 边e2)的引用,并写清整星字段上半星阈值等价于== 5这件事。high_priority、其余视图、流本身都没动。test/hot-lead-threshold-parity.test.ts(新):钉住「视图阈值 == 路由流 hot 分支阈值」。content/docs/sales/leads.mdx+ zh-Hans / zh-Hant:只改 Hot Leads 与 High Priority 两行。.changeset/hot-leads-one-definition-of-hot.md。与 High Priority 的重叠(issue 明确要求回答)
会重叠,而且是完全重叠:改后两个视图的过滤条件逐字相同(
rating ≥ 4+status in (new, contacted)),一条四星线索现在同时出现在两个视图里。这是「只留一个 hot 定义」的代价,不是疏漏,本 PR 选择明写而不是靠调阈值把它藏起来:
两个视图的区别在用途,不在人群,注释与文档都按这个口径写:
「两个视图是否还该并存」是产品决策,不在本单裁定范围内,已另立 finding 单(见下),本 PR 不删不改
high_priority。裁定里那句「五星队列如果是真实需求应以自己的名字另立视图」也写进了代码注释,避免下一个人再往 Hot 上加半星。守卫:两侧阈值都从元数据派生
test/hot-lead-threshold-parity.test.ts(16 个断言)不比较源码字面量,两个数都是从编译后的 stack 里取出来的:stack.views[…].listViews.hot_leads.filter里field === 'rating'的那条子句(operator + value);lead_assignment中target === 'sla_hot'的 conditional 边,从它的 CEL source 里取排序比较(>=/>及其数值;!= null这类总性守卫不计入)。三层断言:
greater_than_or_equal↔>=有显式映射表,未知运算符直接抛,不静默放过)。lead.hook.ts里Math.round(clamped)还在(是它让评分只有整星),再要求阈值是 1..5 的整数。失败信息直说「在整星字段上切 4.5 实际等于 5」。AutomationEngine.evaluateCondition(traverseNext判定边用的同一个方法)问「路由是否判 hot」,另一边把 shipped 的视图 filter 跑一遍问「是否在队列里」,要求两个答案逐行相同。反向真空(这些断言是让上面三层不至于空转的部分):视图 rating 子句数量必须恰好 1、流的 rating 排序比较必须恰好 1、两个阈值必须是 number、真值表里 hot 与非 hot 都必须出现(否则「全 false 对全 false」也能全绿)、视图状态过滤必须包含
new(否则流在入库时判 hot 而队列永远为空,是换了个形式的同一个缺陷)。这个文件不钉
high_priority(第二个 commit 一并说明):改后它与hot_leads过滤相同,唯一可写的断言就是「两者相同」,而那是在给一个还没做的产品决策上锁 —— #775 的任一结论(合并,或用非评分判据重新差异化)都会让它无故变红。有生产者背书的不变量只有「流 ↔ 视图」这一条。另外一条独立断言处理幽灵注释:把
src/views/*.view.ts里所有「snake_case 名 + flow/workflow/automation」(两种词序)的引用扫出来,要求每个名字都能在stack.flows/hooks/actions里解析到。auto_flag_hot_lead这个死名因此从源码里彻底删掉(没有保留在散文里),否则它会命中自己写的守卫。反向验证(方向是先预测再跑的)
预测:这是普通的红,不是反转方向 —— 两侧都从活元数据读,没有「规则本来就没在跑」的情形。
把视图阈值改回
4.5(只改这一处),预测红的三条,实际红的正是这三条:把幽灵注释改回
(Set by the auto_flag_hot_lead workflow.):两次都已还原。
文档同步(#768 刚落地的那三页)
#768 是按旧阈值如实描述的(「实际上的五星队列,四星在 High Priority」),这个描述随本 PR 失效,因此同 PR 顺改,只动这两行 × 三个语言:
leads.mdx:132那条提示(「四星及以上是明天,其余三天后」)说的是流的 SLA,本来就是对的,没动。验证
新增文件单跑:
Tests 16 passed (16)。另用grep -naP对本 PR 全部改动文件做了控制字符自查,干净。第二个 commit(
test(leads): drop the unused high_priority binding…)清掉 CodeQL 报的Unused variable highPriorityView:删掉而不是补一条断言把它用起来 —— 唯一可写的断言是真空/越权的那种(见上)。断言数量前后都是 16,pnpm typecheck与全量pnpm test(1495 passed | 1 skipped)在该 commit 上重跑过。边界
平台版本保持
17.0.0-rc.2,未升级任何@objectstack/*。未触碰:src/flows/lead-assignment.flow.ts(流是生产者,本次让视图跟随它)、src/views/opportunity.view.ts与 dashboards / seeds 注释(#744 在飞)、content/docs/sales/activities.mdx(#747 / #771 在飞)、content/docs/releases/、high_priority视图本身、四个语言包(视图 label 未变)。顺带发现(已另立单,本 PR 不修)
hot_leads与high_priority的过滤条件逐字相同:两个视图是否还该并存是产品决策 #775 ——finding:阈值对齐后hot_leads与high_priority的过滤条件逐字相同,两个视图是否还该并存是产品决策(本 PR 已在注释与文档里写明差异,不自行删改)。Generated by Claude Code