## 问题
水位闸只在**事实变了**时放行重算。这是对的 —— 没有它 40 万患者天天全量重算。
但有一类变化不伴随任何事实变动,时间自己走了就会改档:
急迫等级跨 30/90 天线 · 生命周期跨 180/540/730 · RFM 的 R 跨 540/730/1095/1460
年龄跨段 · 种植年龄禁忌满 19 岁解除 · 时间偏好/特别关注的滑动窗口把最老记录挤出去
这类患者永远等不到重算。年龄段和年龄禁忌最极端 —— 它们**完全不依赖 fact**,
一个 54 岁患者会一直显示「中老年」,直到他碰巧有新事实进来。而年龄还喂着按龄排治疗。
## 解法:算出确切到期时刻,而不是每天盲扫
FeatureExtractor 新增可选 `nextBoundaryAt(ctx)`,报出"纯时间流逝会让我改档的最近时刻";
PersonaService 取全体最小值存 personas.next_boundary_at;夜扫加一条 UNION 分支
只捞 `next_boundary_at <= now()`,走 (superseded_at, next_boundary_at) 索引。
isFresh 同步加这一维 —— 否则夜扫捞出来入队也会被水位当场 noop 掉,白捞。
八个特征声明了边界(rfm / lifecycle_stage / urgency_level / age_bracket /
contraindication / potential_treatment / time_preference / special_attention),
日期算术统一走 features/time-boundary.ts 的四个原语,不手搓。
顺带把 rfm / lifecycle_stage / urgency_level 各抄一遍的「到诊」口径收进 visit-facts.ts
—— 三个标签的 description 都印着"就诊 N 次 / 末诊 N 天",口径对不上客服当场就能发现。
## 本地实测(friday host,13,268 患者)
--force 回填 success=0 · refreshed=885 · unchanged=12,383 → 全量跑几乎不产新版本
边界分布 10,808 有到期时刻 / 2,463 已过全部档界(NULL)
每日到期量 5~30 条(外推生产 40 万约 150~900/天,而不是 40 万)
端到端 人为把某条置为已过期 → 夜扫 SQL 捞到 → 不带 --force 重算 noop=0(闸放行)
→ 内容真没变故判 unchanged 不写 → 边界推进到 2029-06-06,不会重复捞
## 一并清理(逻辑清晰向)
· persona-display.ts 删掉 `[enum] 文本` 前缀解析与 PERSONA_STATUS_ZH —— 服务的四个特征
(treatment_chain_status / recall_risk / value / do_not_contact_status)W7 已摘除,
现存 16 个 extractor 没有一个产这种前缀,抽屉里那个 tag 恒为 null。
· PERSONA_FEATURE_META 从 33 个键收到 16 个(= 在跑的全集)。原来混着已摘除的和只存在于
路线图上的,"看着 30 个标签实际只出 16 个"。枚举保留路线图,展示元数据只登记会上屏的。
· tone 重排:红色只留给警示(急迫/特别关注/禁忌/治疗敏感)。原来「转介绍达人」「折扣锚点」
这类正面或中性信息也是红的,和「禁忌」同色,客服扫一眼分不出轻重。两条 CI 断言守住。
· mock 画像换成在跑的特征 —— 原来摆着六个系统根本不产出的标签,拿它当参照会看错。
· 折扣锚点超过 3 年在详情页给过时提醒(生产上见过 2018 年的锚点被原样展示)。
453 tests green · pac-web build 通过。
## 部署
两个迁移的回填是同一次运行,且**必须带 --force**:
next_boundary_at 的 NULL 语义是"不再改档",不会被判 stale,不强制跑就永远算不出来。
pnpm recompute-persona:prod -- --host=jvs-dw --concurrency=8 --force
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
| Name |
Last commit
|
Last update |
|---|---|---|
| .. | ||
| adapt-data.ts | Loading commit data... | |
| chain-viz.tsx | Loading commit data... | |
| close-opportunity-dialog.tsx | Loading commit data... | |
| drawer.tsx | Loading commit data... | |
| emr-soap-view.tsx | Loading commit data... | |
| execution-api.ts | Loading commit data... | |
| fact-label.ts | Loading commit data... | |
| facts-timeline.tsx | Loading commit data... | |
| mock-data.ts | Loading commit data... | |
| outcome-form.tsx | Loading commit data... | |
| persona-detail-list.tsx | Loading commit data... | |
| persona-display.ts | Loading commit data... | |
| persona-feature-hover.tsx | Loading commit data... | |
| plan-detail-app.tsx | Loading commit data... | |
| plan-detail-types.ts | Loading commit data... | |
| reason-line.tsx | Loading commit data... | |
| script-deep-process.tsx | Loading commit data... | |
| script-viewer.tsx | Loading commit data... | |
| shared.tsx | Loading commit data... | |
| task-drawer.tsx | Loading commit data... | |
| tooth-timeline.tsx | Loading commit data... | |
| use-script-stream.ts | Loading commit data... | |
| use-summary-stream.ts | Loading commit data... |