1. 11 Jun, 2026 28 commits
  2. 10 Jun, 2026 12 commits
    • feat(web): 详情页一页式工作台 — 固定左栏选患者列(4列)+ 画像标签筛选 · 21645e8e
      把"我的任务"hover 抽屉升级为详情页固定左栏(列表页将弃用,核心要素并入;KPI 面板按需求不带):
      
      - plans/layout.tsx:布局挂在动态段之上 — 选中 planId 时渲染「左栏 300px + 详情(原三列)」=4列;
        切患者只重渲染右侧,左栏不重挂(筛选/已加载分页/滚动位置全保留);/plans 列表页不受影响。
      - PatientPickerRail:召回池/我的/全部(权限)tab + 搜索(300ms 防抖)+ 排序 + 真实号码开关 +
        画像标签筛选(popover 四维分组多选:价值分群8/生命周期7/紧迫度3/潜在治疗8,选中 chips 可单删);
        紧凑患者卡(名/性别年龄/真角标/优先级分色/场景/掩码号/状态),触底+按钮双加载;选中 teal 高亮。
      - usePatientPicker:use-my-tasks 泛化版 — 全套筛选上服务端,筛选变化自动回第一页。
      - 后端:ListPlansQuery.personaTags("key:value" 逗号串)→ plan.service 匹配患者当前版画像
        (personas.supersededAt IS NULL)的 features:同维多选 OR、跨维 AND;数组型(潜在治疗)
        用 JSON array_contains;非法 key/value 静默丢弃。
      - @pac/types persona-tag-filters.ts:可筛维度字典(code中文,单一真理源,与 extractor 枚举对齐)。
      - 详情页移除 TaskDrawer(被左栏取代)。
      
      验证:API 对 DB 直查口径一致(重要价值 57==57;OR 173 / AND 10 / 数组 contains 40 / 非法=全量);
      web tsc 0 + Next 生产构建过。仅本地,未部署。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • perf(assistant): 听写提速三处 + 底部提示简化(固定文案) · c52200fe
      服务器实测体感"启动慢"对症:
      1. ASR 容器启动即预热(0.5s 静音跑一遍)— onnxruntime 首推建图/分配比稳态慢很多,
         之前重启后用户第一句要付这笔冷启动。
      2. 网关首句快路径:攒够 ~0.6s 音频立刻出第一个 partial(不等节奏),开口更快上屏。
      3. partial 节奏 700→450ms、tick 250→150ms(inFlight 防重叠不变,服务器解码 ~0.3s 扛得住)。
      
      底部提示按用户简化为固定"结果仅供参考"(去掉听写状态行;修正引号原样渲染)。两端 tsc 0。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • docs: push 契约措辞修订(回执码表精简/示例数值统一)+ W5 周报 · 1a8496a4
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • fix(deploy): pull 后 re-exec 新版脚本 — 本轮才会用上 pull 进来的脚本变更(SERVICES 等) · ab3d047a
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(assistant): 语音输入升级实时听写 — 点击开启,边说边出字,再点纯退出 · 91f79dc6
      交互(用户要求):点 🎤 开始 → 说话时文字实时滚动进输入框 → 再点 = 纯退出(已上屏文字保留)。
      
      实现(复用 realtime-coach 成熟模式,ASR 容器零改动):
      - 前端:PCM16 16k 采音 + RMS 静音门控(无声不发帧)→ socket.io 推帧;
        dictation:partial(当前句滚动覆盖)/ dictation:final(句定稿累加)→ setInput 实时渲染;
        base 保留输入框已有文字,听写追加其后。
      - 后端 DictationGateway(socket.io,JWT 握手鉴权同 coach):按"帧到达间隙"断句 ——
        说话中每 700ms 把当前句 PCM 包 44 字节 WAV 头调 TranscribeService 出 partial;
        停顿 ≥800ms / 超 30s 整句 final 并清缓冲。inFlight 防解码重叠;先清缓冲再 final
        解码(下一句帧不混入)。SenseVoice 离线模型 RTF~0.1 → 句级重解码远快于实时。
      
      实测(模拟浏览器推帧):开口 1.3s 首个 partial,~0.7s/次滚动更新,停顿 1.3s 出整句 final,
      文本与一次性识别完全一致。两端 tsc 0。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(assistant): 语音输入 — 自部署 SenseVoice-small 听写(PII 不出内网) · 5644d1af
      三层:
      - apps/asr-sensevoice:sherpa-onnx + SenseVoice-small int8(纯 CPU,无 torch,镜像 <400MB);
        FastAPI /transcribe:任意浏览器音频(webm/mp4)→ ffmpeg 16k 单声道 → 离线识别(ITN 标点)。
        模型不进镜像/git —— 卷挂载 ${PAC_MODELS_DIR:-../pac-models}/sensevoice(model.int8.onnx + tokens.txt,
        来源 sherpa-onnx releases sense-voice-zh-en-ja-ko-yue-2024-07-17)。内网专用不发布端口(prod)。
      - 后端:POST /assistant/transcribe(全局 JWT 鉴权,multipart ≤15MB)→ TranscribeService 转发
        PAC_ASR_URL(provider 单一出口,以后切云 ASR 改这一处)。
      - 前端:composer 加 🎤(点击录音/再点结束,红色脉冲态)→ MediaRecorder(webm/opus,Safari mp4)
        → 上传转写 → 文字落输入框可编辑再发;错误/录音中提示在底部状态行。
      
      compose:dev + prod 都加 pac-asr 服务(prod 限 cpus:2/mem:2g,阿里云镜像源构建参数);
      pac-service 注入 PAC_ASR_URL=http://pac-asr:8000;deploy 脚本 SERVICES 加 pac-asr。
      
      本地验证:容器直测 3.9s 音频 0.46s 出字"帮我查一下患者孙科的画像和召回计划。";
      经后端鉴权链路同样通过;无 token 10106 拒。两端 tsc 0。暂不部署服务器(模型待 scp)。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(alerting): 企微 webhook 告警 + 增量空转探针 + DW 滞后告警接通 · 4c93c669
      背景:增量游标 ISO 格式 bug 空转三天才被人工发现 — "success+fetched=0" 无人知晓。
      
      - AlertService:识别企微 webhook(qyapi.weixin.qq.com)→ markdown 格式(级别 emoji/颜色,
        context 引用块,body 截断);企微 HTTP 永远 200 → 检查 body.errcode 才算送达。
      - 增量空转探针(ColdImportService 收尾):带游标的增量跑出 0 写入时,反查 DW
        「比游标新的行数」— DW 也 0 = 真没数据(静默);DW>0 = 拉不到但有 → 当天 critical 告警。
        探针(ClickHouseSourceService.probeNewRowCounts)任何异常吞掉,绝不影响同步主流程。
      - DwLagMonitorService:🟡/🔴 滞后从"只打日志"接到 AlertService(企微同收)。
      
      验证:本地用编译产物真发企微群,payload 被接受(无 errcode 报错)。tsc 0。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • fix(sync): 增量游标 ISO 格式与 DW String 列字典序比较不兼容 — 增量三天空转 · 7375f010
      诊断(2026-06-10 服务器):增量 cron 每天 success 但 fetched=0 × 3 天;DW 实际有新数据
      (fact_appointment_out max updated_date=06-09 22:50)。根因:DW 的 cursor 列是
      Nullable(String)('YYYY-MM-DD HH:mm:ss'),WHERE 比较走字典序;游标存的是 run_start ISO
      ('2026-06-09T03:30:00.012Z'),第 11 字符 'T'(0x54) > ' '(0x20) → 所有真实行字典序都
      小于游标 → 永远 0 行。实测同一时刻 ISO 谓词 0 行 / 普通格式 1178 行。
      
      修复:读取侧规范化(toDwCursorLiteral)— 构造 IncrementalConfig 时把 ISO 游标按
      manifest.timezone 转成 'YYYY-MM-DD HH:mm:ss'(sv-SE locale 恰好此格式);
      旧游标无需迁移;已是普通格式/垃圾值原样返回;秒精度(同秒 .SSS 行值字典序更大仍被选中,
      轻微重叠由 source_event_id 幂等吃掉)。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • fix(deploy): 逻辑包进 main() — 脚本 git pull 自更新时 bash 边读边执行会错位 · bd442595
      bash 按字节偏移惰性读脚本;本脚本运行中会 pull 更新自己 → 后半段按新文件偏移执行旧/错位代码
      (实测:验证逻辑跑的是旧版)。包进 main() 并在文件末尾调用,bash 先整体解析完再执行,免疫自更新。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • fix(deploy): migrate status 校验改先落变量再 grep — pipefail+grep -q SIGPIPE 误报 · 55444ced
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • fix(deploy): migrate status 校验不再 tail 截断 — prisma 版本提示框位置不固定致误报 · c8c6943a
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • fix(deploy): 确定性生产部署脚本 — 根治 compose "不切新镜像" 缺陷 · 87741f59
      docker compose `up -d --build` 有跨版本反复复发的已知缺陷(docker/compose#9308 #9259):
      镜像构建成功但运行中容器不重建(仍跑旧镜像)。本项目 2026-06-10 部署实测中招:
      service 容器还在旧镜像 → migrate 报 "no pending" → phone_verified 列没建。
      
      根治思路:生产部署不依赖 compose 的 diff 启发式判定。deploy/deploy-prod.sh:
        git pull --ff-only → 显式 build → up -d --force-recreate → 三道硬验证
        (① 容器镜像 ID == 新构建镜像 ID;② migrate exit 0 + migrate status 无 pending;
         ③ service health 200 + web 200),任一不过即非零退出。
      README 把日常更新指到脚本,并警告勿直接 up -d --build。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed