1. 07 Jun, 2026 30 commits
    • fix(recall): v3 优先级加诊断新鲜度衰减(老诊断止损)— 修重做时丢的 timeWindowFactor · cd4303ff
      v3 重做时丢了旧 scorer 的过晚衰减:新模型急迫性按【末诊】算、不看诊断年龄,
      导致很多年前的老诊断(末诊也久)反而 urgency=紧急、分还高。而入池无时间上界(W3:缺口不自愈
      仍入池),当初就靠 scorer 衰减止损 → 丢了就没人止损了。
      - 加 computeFreshness(daysSince, windowDays):黄金窗内 1.0;过窗线性衰减到 2×窗=0.4,地板 0.4。
        per-病种(K08窗180/K07窗365 → 慢病衰减慢)。软衰减不硬切(同 W3:止损交 scorer+UI排序+客服自选)。
      - 综合 = (急迫×0.4+价值×0.3+意愿×0.3) × 新鲜度。breakdown 加 freshness/base 可解释。
      - 本地:58% gap 有衰减(池里大量老诊断);老 missing_tooth base8.5→分42,新鲜的照常高分。
      - 注:PAC 无丢单数据(v3.0 靠已丢单止损),新鲜度衰减是 PAC 的止损替代。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(recall): 优先级算法重做为 v3.0 三维模型(急迫×0.4 + 价值×0.3 + 意愿×0.3) · ec55b5be
      替换旧 6 因子加权启发式(只用 value+risk 2 特征)为业务《画像字典 v3.0》优先级模型:
      - 急迫性 = urgency_level(紧急10/高7/中4/低1)
      - 价值性 = 治疗类型+牙数(种植 单颗8/多颗9/半口10 · 正畸7 · 根管6 · 修复5-6 · 牙周5 · 拔3 · 补2)
      - 意愿度 = RFM依从×0.375 + 主诉行为×0.375 + 信任基础×0.25
        · RFM依从 = rfm 八象限分群
        · 主诉行为 = 咨询过该类(consultation 意向命中)→8 / 仅诊断→2(触达活跃维度 PAC 无数据,去掉按比例归一)
        · 信任基础 = lifecycle + 同类治疗史+1 + 转介达人+1
      - scorer 用上 6 个画像特征(rfm/urgency/lifecycle/treatment_history/referral + consultation 意向),旧版只用 2 个。
      - ️ 只改 priorityScore(排序),不改召回候选集(gap 选择独立)→ verify-recall FP/FN 不受影响。
      - 本地 928:候选 589(≈旧588 不变),分数 23-94 avg66.6;价值序 种植78>牙周/根管/正畸73>拔/补67>发育62。
      - 局限(v3.0 同,记 follow-up):权重人拍(可调);意愿≈倾向性非增益(uplift,需试点对照组);
        confidence<0.6 降权 + 触达维度 待数据/二次筛选实现。breakdown 保留可解释。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • docs(persona): 16 标签(删 do_not_contact_status)+ lifecycle 咨询 gate + 咨询主体/treatment_intent follow-up · cca5ef37
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • refactor(persona): 删 do_not_contact_status 特征(免打扰并入 special_attention)+ lifecycle 潜客加咨询 gate · 539dbcf2
      - 删 do_not_contact_status extractor(免打扰已在 special_attention/CDP D.2.3;合规闸召回读 profile 原始列,
        不依赖本特征)。enum 标弃用保留(前端 hover/label 无害)。已故/投诉信号后续补(投诉数据本就缺)。
      - lifecycle 潜客(B.1.4):零就诊 → 加 gate「有预约 OR 有咨询」(用上新摄的 consultation_record);
        零就诊且无触点 → 兜底新客(不落 lastDays 分支避免 Infinity 误判流失)。
      - 本地 928:现役 16 特征(dnc 0),lifecycle 成熟614/新客207/成长104/待激活2/流失1
        (样本无零就诊 → 无潜客,全量会出)。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • fix(ingest): consult SQL 还原朴素(CSV 双源等价)— concat 移到 transforms.derive · fdbe3424
      - 违反了 manifest 纪律(SQL 只做朴素导出,形态改造归 transforms):consult_external_id 的 concat
        从 SQL 移到 transforms.derive(I 段),SQL 回归 SELECT 列 + WHERE 过滤。
      - organization_id IN(过滤无关诊所)+ cohort patient 过滤 保留(合法 WHERE,导出本就按诊所过滤)。
      - 审计其他表:均朴素(SELECT 列+WHERE);image_finding_rows 是文档记录的已知例外(影像AI pivot,CH 限制)。
      - 重摄验证:5993 facts/0 failed,subject_id 与 concat 版一致。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(ingest): 摄入咨询主体 consultation_record(fact_consult_out,5 试点意向源) · 956a42d0
      - 定义 PAC 咨询主体:一次咨询/初诊事件;intents=患者意向(potential_cure,主观意愿,非诊断)。
        区别 diagnosis(医生客观)/recommendation(医生建议)— 意向不进召回,喂 treatment_intent。
      - 链路:fact_consult_out 全 144 诊所,过滤到 5 试点(org IN EMR orgs + patient_register_id=patient_id,
        CH 允许 WHERE 引用别名,同 returnvisit);94.4% 命中。无 id → consult_external_id=(patient,appo,date) concat;
        无 updated_date → 不入 per_query,每轮全量按 org+patient 过滤(幂等,同 returnvisit)。
      - canonical-codes:CONSULT_INTENT_TO_CATEGORY(种植→implant…拔功能牙→surgical/早期矫正→ortho/美白→cosmetic)
        + parsePotentialCure(解析 Python list 串)。parser/schema/assembler/manifest 配齐。
      - 本地 928:5993 facts,0 未映射,失败0。意向类别:正畸1006/种植733/预防383/充填350/根管316…
      - ️ map 改后需 truncate consult+重摄才生效(reparse 缺口 task #46);部署是干净全摄,自动生效。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • docs(persona): 补 urgency_level(17 标签)+ 急迫已治疗复查路径 follow-up · 2cdab12b
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): 急迫等级特征(C.2.1,v1 仅潜在治疗路径) · efd767e5
      - urgency_level 单标签(取最大):有潜在待转(8 业务标签 gap)且 末诊>90天→紧急/30-90→高/<30→中。
        末诊口径同 lifecycle(encounter/actual treatment/挂号 max)。
      - ️ v1 跳过【已治疗复查路径】(召回未实现复查场景,follow-up)。美学/预防→低(8标签不含,暂不触发)。
      - 抽 classifyGapToLabel/ageYearsAt 为共享函数:potential_treatment 出标签 + urgency_level 判待转 共用(单一源)。
      - 本地 928:紧急384/高223/中164=771,与 potential_treatment 完全一致。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • docs(persona): 补 potential_treatment(16 标签)+ gap 核心共享架构 + 召回种植年龄门 follow-up · e077c6ce
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): 潜在治疗特征(C.1.1,复用召回 gap 核心) · 92f32b19
      - 新建 PotentialTreatmentSelector(clinical-gap):per-patient gap = 召回 gap 核心(共享 buildGapCore)
        去时间门/合规门(常态属性);按 active 诊断/建议码剪枝。
      - potential_treatment 画像特征:gap → 8 业务标签(种植←K08>18 / 补牙←K02 / 根管←K04 /
        牙周←K05,K06 / 正畸←K07>12≤40 / 早矫←K07 3-12 / 修复←K03默认 / 拔牙←K01+K03残根残冠)。多标签。
      - 码分组单一源 GAP_PRIMARY_GROUPS:召回 SUB_SCENARIOS 不再内联 dxCodes/recCodes,改引共享。
      -  召回字节等价再验(Phase2 码分组改后 EXCEPT 双向 0 diff,1450 全等)。
      - 本地 928:771 患者有潜在治疗(拔牙433/补牙341/修复289/种植184/牙周150/正畸105/根管97/早矫24),
        是召回候选超集(无时间门);正畸+早矫129<召回163 因 spec 正畸封顶40岁(符合预期)。
      - 置信度=诊断1.0/建议0.8(复用)。非已丢单 PAC 未摄入,省略(follow-up)。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • refactor(recall): 抽 gap 核心到共享模块 clinical-gap(为潜在治疗画像单一源) · 76516429
      - 新建 modules/clinical-gap/potential-treatment-gap.sql.ts:toothArrSql + buildGapCore
        (sig 牙位/resolved/remaining 相减 + ⑤a 判定 + 废用牙/先天剔除 + §E flag),Layer 1 facts 消费层(中立)。
      - §E gap flag 单一真理源 GAP_FLAGS_BY_PRIMARY(召回 SUB_SCENARIOS 不再内联)。
      - 召回 scenario 改 import 共享片段,SQL 逻辑零改动只搬家。
      -  字节等价验证:新旧码召回集合 EXCEPT 双向 0 diff(本地 928,1450 条全等)。
      - 目的:Layer 2 潜在治疗画像将复用同一 gap 核心(避免 Layer 2→3 倒置 + 口径漂移)。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • docs(persona): 补 treatment_sensitivity + contraindication(15 标签)+ 审查发现/Layer C · bfaae12d
      - 总览表加 治疗敏感 / 禁忌标签;13→15 标签;多标签 + 稀疏 + 未完整实现(Layer C)说明。
      - 审查发现 +2:treatment_sensitivity 关键词精炼排假阳;contraindication 不上规则版(否定泛滥/
        量化无数据/需临床判断 → 仅种植年龄,余留 Layer C)。
      - follow-up 加 Layer C(LLM)层标签:禁忌完整版/沟通偏好/意向/家庭社交。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): 禁忌标签特征(D.2.4 v1,仅种植年龄禁忌) · 4f5df5c8
      - contraindication 多标签结构(种植/正畸/麻醉/手术禁忌)。v1 仅实现【种植年龄禁忌:年龄≤18,
        骨骼发育未完全】,validUntil=满19岁自动解除(到期重算即不打)。
      - ️ 其余禁忌不上规则版(会医疗事故):既往史'过敏'97%是'否认过敏'(否定泛滥)、HbA1c全量仅10条
        (量化条件无数据)、需控制状态/急性期判断 → 留 Layer C(LLM 抽取)。data 结构已留多标签位。
      - 本地 928:种植禁忌 167,validUntil 正确,交叉验证 age_bracket≤18 一致。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): 治疗敏感特征(D.2.2,病历关键词精炼) · ff44c92b
      - treatment_sensitivity 多标签:看牙恐惧/晕针/晕血/密闭恐惧。扫 emr_record.content
        (past_history 既往史/illness_desc/pre_illness/disposal 等)+ profile.notes/tags。
      -  关键词按全量数据排查精炼:看牙恐惧 恐惧/害怕看牙/牙科焦虑/看牙紧张(裸紧张=颏肌紧张);
        晕针;晕血/见血不适(裸见血=可见血凝块);幽闭/密闭/长时间张口不适(裸张口受限=物理受限)。
      - 宿主未给 health_profile;EMR 既往史已承载(晕针病史等)。无迁移/无重摄。
      - 本地 928:看牙恐惧 2(全量约 177 患者 0.13%,低覆盖高价值安全标记)。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • docs(algorithm): 重写画像层 v2 为实现版(13 标签 + 架构 + 算法审查发现) · 5c7e097b
      旧版停在早期设计(RFM几何八象限/简化lifecycle/圈人群),跟最终实现对不上。
      重写为:画像层定位 + persona schema + 4 数据范式 + 时间语义 + Feature Registry(指向
      persona-feature-specs.ts 单一源)+ 13 标签总览表 + 算法审查发现/口径决策(RFM图口径/
      lifecycle顺序bug/discount金额非比率/TZ/no_show不在active等)+ follow-up + 部署清单。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): 特别关注特征(D.2.3,屡次爽约/经常迟到/免打扰/不可等候) · 90ca7f57
      - special_attention 多标签:屡次爽约(近1年履约率<50%且决定≥3)/经常迟到(到店>预约+15min≥50%
        且≥3)/免打扰(doNotContact)/不可等候(notes/tags/病历关键词)。
      - ️ 挖出 2 个真问题并处理:
        ① no_show/cancelled 预约 patient_facts.status≠active/fulfilled,不在 persona ctx → 新增
           ctx.appointmentsAll(单独加载全状态预约,排 superseded)给爽约/迟到用。
        ② arrived_at(in_time)摄入 TZ bug:比 planned_for 一致早 8h → +8h 补偿(根治修摄入,follow-up)。
      - 本地 928:经常迟到86/屡次爽约2(免打扰/不可等候样本无数据,全量会出)。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): 折扣锚点特征(D.1.3)+ 修 spec naive 点 · 669f4fa6
      - DW 无 original_amount;discount_*_rate 实为折扣金额(应收3-dept0.45=实收2.55)→
        折扣率=1-Σ折扣/应收。摄入 payment_record.content.discount_cents + settlement_project(重摄)。
      - discount_anchor:取真实治疗最深【部分】折扣 + 日期/项目。
      - ️ 修 spec 两个 naive 点:① 免费洁牙/检查促销(100%off=0折)会霸占锚点→只看原价≥¥500+ratio>0;
        ② 保险方付款非诊所折扣→排除 discount_insurance_rate(只算科室/公司/卡)。
      - 本地 338 患者:avg 6.4折,样例合理(种植9.4折/修复/促销2.9-5.1折)。
      - ️ 留口径:近免费(>90%off)comp/全保 是否算锚点;¥500 阈值可调。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): 时间偏好特征(D.1.2) · 5210f3ea
      - time_preference 多标签:工作日/周末/上午/下午/晚间偏好。近2年预约,北京 TZ。
      -  数据排查:occurred_at 钟点被搞乱(时区/解析,16-23 乱分布),planned_for +8 才是
        干净营业钟形(8-18 峰 9-15)→ 用 planned_for。无新摄入/无迁移。
      - 阈值照 spec:工作日≥60%/周末≥50%/各时段≥50%,记录≥2;无命中→不打标签。
      - 注册表 spec/enum/label。本地 601 患者:下午368/工作日305/上午289/周末289。
      - ️ follow-up:appointment occurred_at 钟点不可靠(latent bug);TZ 硬编北京,多宿主应读 host TZ。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): 治疗史特征(C.3.1,读 canonical category) · 35170610
      - treatment_history 多标签:implant→种植史/orthodontic→正畸史/prosthodontic·cosmetic→修复史/
        periodontic→牙周治疗史。不标记 surgical/restorative/endodontic 等基础治疗。
      -  spec 用病历/结算关键词匹配(数仓原始文本);PAC canonical 层已归一成 category →
        直接读,无关键词/无新摄入/无迁移。canonical 层价值体现。
      - 注册表 spec/enum/label。本地 773 患者:牙周596/修复195/正畸165/种植120,多标签生效。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): 生命周期特征(B.1.4)+ 修 spec 顺序 bug · 57838efe
      - lifecycle_stage 7 阶段(潜客/新客/成长/成熟/待激活/沉睡/流失)。PAC 自算:
        net_receipts_total/total_visit_times 源表(fact_client_out)没有 → 从 payment/就诊 fact 算
        (同 RFM 口径);潜客用 appointment fact。无新摄入/无迁移。
      - ️ 修正 spec 顺序 bug:原'沉睡>540'在'流失>730'前→流失永不触发;流失提前到沉睡前。
        本地验证:末诊1913天/23次患者正确判流失(原顺序会误判沉睡)。
      - 移除 rfm.data 里的简化 lifecycle(避免与本特征重复;生命周期归本特征单一来源)。
      - 注册表 spec/enum/label。本地:成熟614/新客207/成长104/待激活2/流失1。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): 权益身份升级为 B.1.3 五类多标签(卡券关键词) · 6689b612
      - 摄入 fact_settlement_mode_out.card_type_name/card_name → payment_record.content
        (payment.parser + schema + payment.yaml;String() 强转防数字型卡券值炸 .trim)。
      - entitlement_status 重写:遍历结算卡券/保司/channel 关键词多标签(允许并列):
        高端保险直付/银行私行权益/储值会员/儿牙会员/医保客户;未命中→不打标签。
      - 注册表 spec(v2)。本地 928:医保225/银行私行175/儿牙35/储值27/高端保险17(274 患者有权益)。
      - 无 schema migration(卡券名落 content JSONB);需重摄 payment facts。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): 转介绍达人特征(B.1.2 v1,DW 预聚合) · 37527216
      - 摄入 fact_client_out.recommend_num/recommend_amount → 副表立柱 referral_count/referral_amount_cents
        (元→分;canonical schema + patient.yaml + cold-import/dispatcher 两路 upsert + migration)。
      - referral_champion 特征:门槛 recommend_num≥3 且转化额>0;直系家庭≥3→家庭型,否则社交型;不满足→不打标签。
      - 本地 928:26 社交型(家庭型需≥3直系家庭,样本稀疏未出)。
      - ️ v1 用 DW 预聚合替代逐个被推荐人'均有效转化'(跨患者+需被推荐人已摄入,留 v2)。
      - ️ recommend_num 增量刷新滞后(fact_client_out 游标=last_visit_time,无 updated_date):
        推荐人下次到诊/全量重摄才更新;真修=游标改 greatest(last_visit_time,recommend_last_visit_time)(follow-up)。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): 家庭构成特征(A.3.1,PatientRelation 反推) · a99b88d7
      - 复用 PAC PatientRelation 边表(无迁移/无重摄):relations 注入 persona ctx,
        family_structure extractor 反推直系亲属结构。
      - 直系 spouse/child·grandchild/father·mother·grandparent;sibling/friend/other 噪音不计。
        有长辈→多代/有子女→多口/有配偶→两口/有非直系边→单身/无边→不打标签(数据缺不臆断)。
      - 注册表 spec/enum/label。本地 85/928:多口28/多代21/两口19/单身17。
        ️ 覆盖依赖关系边,样本稀疏+非全量偏低;多代采广义(有长辈即跨代)。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): 获客渠道特征(A.2.1)+ 副表立柱摄入 · 28783dde
      - DW fact_client_out.primary_category/sub_category(L2 初诊来源)→ 摄入 PAC 副表
        patient_profiles.acquisition_channel(PAC立柱标准枚举)/acquisition_sub(host原值)。
      - 链路:canonical-codes.PACAcquisitionChannels(单一收口)+ patient.yaml enum_mapping(走入→walk_in 等)
        + PatientCanonicalSchema + 两条 upsert 路径(cold-import 全量 / dispatcher 增量)+ migration(2列+索引)。
      - acquisition_channel persona 特征(snapshot,读副表立柱出标签)+ 注册表 spec/enum/label。
      - 本地重摄 928:口碑331/走入162/集团营销158/地区营销157/电商65/集团销售53/自媒体2,特征100%覆盖。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): 性别特征(A.1.2,snapshot) · 48c60a48
      gender extractor:patient.gender 映射 M/男→男性、F/女→女性、其他→未知(照图,三值)。
      注册表 spec + enum/label;来源可切(现 patient.gender,宿主给 client_gender 后切)。
      本地 928:女 468 / 男 460,画像现役 5 特征(rfm/age_bracket/gender/dnc/entitlement)。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): 年龄段特征(A.1.1,snapshot) · 19793597
      - age_bracket extractor:从 patient.birthDate 算周岁 → 9 档(婴幼儿..老年),照图区间;
        3/55 重叠按'下界含、归下一档'+ ≥55→老年消歧。snapshot 时间语义(历史读版本流)。
      - 注册表 spec(标签卡)+ enum/label;数据来源可切(现 birthDate 自算,宿主给 client_age 后切)。
      - birthDate 缺失/年龄越界(<0/>120 脏数据)→ 不打标签。
      - 本地 928 验证:分布合理、边界消歧正确、覆盖率 100%。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): 翻转 scorer 读 rfm.data + 摘除旧 value/recall_risk/treatment_chain · 1b086e7b
      - treatment-initiation-recall.fetchPersonaContext:改读 rfm.data(valueTier/riskScore),
        rfm 缺失优雅回退旧字段。本地 1437/1437 plan 打分零变化 → 翻转行为等价。
      - FeatureRegistry/persona.module:摘除 value/recall_risk/treatment_chain_status 三个 extractor
        (treatment_chain 降级为详情页 episode 视图,后续单做);现役 rfm/dnc/entitlement。
      - rfm 决策树补全:develop 档 F=2 扩 F<3(近期单次新客→一般发展,不误落低活跃)。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): RFM 采用业务 CDP 口径(图 B.1.1)+ M 分位 + 注册表 spec · 3a3abed7
      - rfm.feature 改为业务整理好的 RFM 定义:R/F 分段照图、M 按租户分位(p20/40/60/80)、
        8 段决策树(重要价值..低活跃)。R/F/M = last_visit_time/visit_times/net_receipts_total(lifetime)。
      - M 分位需群体计算:PersonaService 算+缓存租户分位阈值(30min TTL),注入 ctx.populationStats;
        缺失降级绝对¥档。valueTier(绝对¥)/riskScore 保留 → 仍 100% 兼容旧 value/recall_risk。
      - 新增 persona-feature-specs.ts:标签注册表(标签值/数据来源/数据字段/释义/算法/时间语义),
        代码存、来源可切(现 PAC 自算,宿主 CDP 报表给出后切宿主值)。score 列弃用语义。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): RFM 八象限特征(统计层,additive)+ recompute --force · 6fafb50e
      - 新增 rfm feature(融合 R最近/F频次/M金额三时间语义):data 带 segment 八象限 +
        lifecycle 生命周期 + valueTier(0-4)/riskScore(0-3)。统一旧 value+recall_risk。
      - additive 接入(暂不动 scorer/旧特征);本地 928 验证:分布临床合理,且
        valueTier/riskScore 100% 复现旧 value.score/recall_risk.score → 后续翻转零风险。
      - recompute-persona 加 --force:算法/特征变更后(数据没变)跳过水位幂等闸强制重算
        (部署到服务器也需要,否则全 noop)。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • docs(algorithm): 画像层设计 v2 — 时间语义/Feature Registry/RFM 八象限/圈人群 · 0edbe90d
      画像从'被动打分+话术标签'升级为有治理、有时间语义、可圈人群的特征体系:
      - 时间语义模型(snapshot/window/lifetime/trend,每特征声明;画像=压缩当前态,历史留fact层,版本流=point-in-time)
      - Feature Registry 单一收口(OneModel)+ CI 防漂移
      - persona_features 加 typed value JSONB(支持 SQL 圈人群)
      - RFM 八象限(统一现有 value+risk:M→value,R+F→lifecycle/risk)+ 召回语义映射
      - lifecycle_stage 生命周期分层;treatment_chain 移出画像→episode 视图
      - Campaign 圈人群→批量召回;质量监控;分 5 PR 实施
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
  2. 06 Jun, 2026 10 commits
    • fix(plan-detail): 深度/标准话术刷新丢失 + 主治医生误显影像AI · 4c3f9c58
      - parseScriptMarkdownToSections 改通用 H2 解析:原只认稳健 4 固定标题(开场白/...),
        深度/标准的自由标题反 parse 全落空 → plan_scripts 存了内容但刷新显示'尚未生成'。
        现按任意 ## 标题切段(稳健固定标题映射已知 id,标准/深度自由标题用 s{n}+原标题),三档通用。
      - KeyFactsCard 主治医生:影像AI(image_ai 触发诊断)不当主治医生显示,退回真实最高频医生
        (排除影像AI/image_ai),真无人类医生 → '—'。话术侧 extractPrimaryDoctor 本就排除,一致。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • docs(algorithm): 召回正确性验证 + §D/§E 修复 + 扫描器用法留档 · 5816aca8
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • verify-recall: FN 分解加 §E 剔除层(智齿/先天/正畸减数)+ 改 CASE 归桶 · c37f0e41
      §E 三条剔除(已落地 scenario)之前会被扫描器虚报成 truly_unexplained;补 x_thirdmolar/
      x_congenital/x_orthoextract 三 flag,FN 按首个命中归桶,truly_unexplained 才准。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • fix(recall §E): K08 缺牙召回剔除空位但非真缺牙(影像AI/name_map 误召) · 0940c635
      仅 missing_tooth 子场景加 3 条剔除:
      - excludeThirdMolar:智齿位 18/28/38/48 不召种植
      - excludeOrthoExtractionSites:该牙有外科拔除 + 患者正畸语境(K07/正畸治疗)= 正畸减数位,
        缝隙靠矫治关闭,不种植(折进 resolvedTeeth 按牙减,忽略时间方向:拔在 K08 诊断前也算)
      - excludeCongenitalName:name 含'先天'(先天缺失等)→ 正畸统筹开/关隙,不自动召修复
      本地验证:智齿位 active 召回 2→0,3573063 '38;46'→'46'(精准保留真磨牙),无过度抑制(FP 仍1)。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • perf(verify-recall): 扫描器改集合式(unnest 进索引临时表)— 全量秒级(原逐行相关子查询 13万患者卡死) · f4f7a212
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • fix(recall): 全口码治疗后复发重召 + 召回正确性扫描器入库 · 98575181
      - treatment-initiation-recall:excludeIfEverTreated(K05牙周/K07正畸)从"曾做过即永久排除"
        改为"治疗须晚于该病【最新诊断】才算已处理"。最新诊断在末次治疗之后(复发未治)→ 重新召回;
        维护中患者(末次治疗≥最新诊断)继续排除,活跃患者仍由 cooldown/⑤b/⑤d/⑤f 兜住。
        仅改两段时间方向 fragment + 加 latestDxOfCode 子查询。
        本地 1000 患者验证:复发漏召 27→6(残留全是 cooldown内/已指派锁/⑤d 合法不召),牙位级 FP 无回归。
      - 新增 sql/verify-recall.sql:只读召回正确性扫描器(FP 硬闸 + 牙位交叉表 + FN 逐层解释 +
        全口复发 + K08正畸语境),每次摄入/改算法后跑做回归。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(auth): 模拟登录接入诊所客服名册 + 列表诊所筛选按可见范围 · 379a4af8
      - 数据:data/jvs-dw/users.json — 116 位在岗客服(回访表口径·近12月),
        结构 = 未来 users/user_clinics 两表形状(externalId/name/tenant/roles/clinics)
      - 后端:mock-users.ts 加载器(cwd 解析+缓存);mockLogin 支持 userExternalId
        (选具体客服→sub=externalId/真实姓名/该客服诊所);新增 GET /auth/mock-users
      - 前端:快速登录改为「角色(权限)+ 选客服」合并式;名册拉不到时降级回通用网格
      - 前端:列表页诊所筛选选项改用 user.clinicIds(RBAC 可见范围),名取 dictionary
        (原误用全 tenant 名表,与后端 scope.clinicIds 过滤对齐)
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(web): 话术 toolbar 选择/触发分离 + 原文流式展开 + 分档 loading 骨架 · 3725c9ad
      - RegenBtn:[档位▾][模型▾][重新生成] —— 两下拉只选不触发,重新生成移右为唯一触发口。
      - 原文视图:默认展开语义(只记主动折叠)→ 流式时新段(s0/s1…)也展开,不再折叠。
      - loading 骨架分档:稳健 4 段固定标题 / 标准 4 段无标题 / 深度 1 段(段数不定)。
      - AIStamp 精简(去图标+label,只留相对时间)。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(script): 标准去模板/深度富输入+质量分/tone 收拢/治疗计划 + 资深审查修复 · c29f18c7
      标准档:4 固定角色字段 → 自由 sections[](固定4段,复用 DeepDraft + 段渲染 +
        段数组安全规则);format.md 去 tier 污染纯指令;流式(段数组 partial 边渲染)。
      深度档:富输入 base++ — 历史联系(patient_return_visits 最近5条)+ persona 价值/流失风险,
        经 buildDeepExtensions 进 plan/write/verify;plan 放开结构(去固定段式、允许其他牙开段);
        verify 加 quality 质量评分(1-5,非 gate,回填 judgeScore/judgeRubric 供 eval);format 去污染。
      治疗计划:ScriptMedicalRecord.plannedTreatments(treatment_record planned)— 原只读常空的
        emr.treatment_plan 导致话术缺治疗计划;stable/standard 渲染;standard 病历补全到全 SOAP。
      tone 收拢:shared/tone.ts 单一源(枚举/describe/label);选择规则只留人群 SKILL(system),
        user prompt 只给信号(熟络度),模型判断。占位:去模板档去 {} 替换占位,只留【时间段】人工填。
      
      资深审查修复:
      - P0 SSE 客户端断连无清理 → AbortController 串到底(controller→orchestrator→runner→
        generateObject/streamObject abortSignal):断连即取消在途 LLM(深度3-4步不再白烧),
        abort 不兜底/不写 PlanScript。实测:深度 6s 掐断仅 +1 条 aborted、PlanScript 未脏。
      - P1 深度质量分回填到最终 invocation(原挂被丢弃的草稿);深度非流式 cacheHit 真实累计
        (原硬编码 false);extractPrimaryDoctor 注释更正;skill-registry 陈旧 env 名更正。
      
      两端 tsc 通过;三档 dry-run + 流式 + 中断 均验证。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • chore: gitignore docs/.obsidian(本地 Obsidian 工作区不进版本库) · 0f3665d6
      workspace.json 等每次开 Obsidian 都变动,反复污染 git status。
      untrack 整个 docs/.obsidian/(本地文件保留)。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed