# 录音实体展示

实现从 official/dev `284273d520bcc1410cb9b758c85e623e10f7b6bd` 开始，提交 PR 前已同步 `6d054ac8`。仅修改插件；客户端 master `e80899460886786bb2f51329b0f6fde8e2944fda` 继续通过现有 Browser module 和 requiredPlugin 装配。DSH 参考版本 `dsh-v0.1.5-rc.2`，仅使用公开 Host/Tools API。

Relation 持有实体、提及和发布版本，Audio 持有转写原文。列表与提及查询只读；用户明确点击「开始识别」时，通过独立写入口调用 Relation 幂等 ensure，不修改转写状态。

| 消费面 | 实现合同 | 验证目标 |
| --- | --- | --- |
| Host | RecordingEntityService 统一账号、版本、引用、手动启动和错误语义 | 失效版本、跨账号、原文位置、部分结果 |
| UI | 当天实体、九类筛选、连续滚动提及、原文定位 | 实际 Harness 页面和交互测试 |
| Tools | arkme_recording_entities 只读查询；arkme_recording_entities_start 显式用户写授权 | 正式注册、真实会话发现和调用 |
| SDK | 能力检测、公开 DTO、查询、startRecordingEntities 与 AbortSignal | 独立包 Consumer 编译和调用 |
| Client | 现有装配合同，无原生桥变化 | 独立 Electron 开发客户端、隔离 App Data，真实测试服账号验收 |

请求 owner 为 Host 统一协调器；UI 只持有当前日期的结果和当前展开实体的已读取提及；提及传输每次最多 50 项，滚动到末尾自动追加。实体与提及复用 ArkmeDirectoryWindow，以 20 项分块、视口前后 600px 窗口和测量占位保持滚动范围；仅额外保留展开或键盘焦点所在块，不把所有已读取条目累积为 DOM。上游日结果最多接受 5000 个实体、单实体 10000 个提及，超过限制显式报错，不能伪装完整结果。面板只在前台可见且后端 pollable 时轮询，5 秒间隔、失败最多退避至 30 秒、连续 3 次失败或 10 分钟停止并提供手动刷新。按 revision 复用当前结果；切账号、日期、关闭或卸载取消请求和定时器。

提及引用加密且绑定环境和账号。Relation 提及和 Audio 原文并行读取（每次最多两个并行请求，任一失败取消另一个），全部完成后重新核对当前日版本和账号；按原始行身份、来源、Unicode 码点和原文子串验证，再映射到清理后的展示文本。不按相近时间或词语搜索猜测目标。原文读取复用 Audio 请求与转写投影，不加载提及展示用不到的说话人列表、资料和头像，也不引入结果缓存。实体原文读取保留 Audio 当日所属录音的完整会话，包括跨过午夜的后续转写；普通日期列表仍按当天范围显示。跨日提及在通过同样的身份、版本和 Unicode 子串校验后，返回可选 target.sourceText，UI 点击时展开带完整日期的原文并高亮精确位置。该字段经现有 occurrences Host/Tools/SDK 返回，不新增查询和后台任务。原文删除或变更后仍不可定位，不能用同名或近似时间替代。

实体入口采用用户选定的 B 方案 Graph 实体关系图标，位于转写操作栏的“转写对比”右侧、“导出”左侧；保留提示、无障碍名称和展开状态，数量显示在实体面板内。

转写默认按当天已识别的实体名称，对同名文字添加下划线，无需先打开实体面板。此标记不代表每处同名文字都有后端提及坐标，也不用于猜测原文跳转目标；点击提及仍经过原文与版本校验，只对该精确位置额外叠加底色、边框和加粗下划线。名称按字面匹配，拉丁字母和数字保留词边界；重叠位置优先较长名称，同名按实体列表顺序选择，相邻不同实体保留独立身份。位置使用 Unicode 码点，与搜索高亮叠加时保持原文不变。默认标记复用已有当天实体列表，在账号、日期或实体结果变化时更新，不额外请求每个实体的提及。

点击下划线文字会打开已有实体面板，仅清除不匹配的类别筛选，并将该条目滚到列表上方、展开提及；通过实体 key 定位，不从点击文字反向猜测实体。用户从工具栏重新打开时保留当前筛选、展开项、已读取提及和列表位置。Enter / Space 可打开，Escape 可关闭并恢复焦点；点击和双击不冒泡为录音播放或片段多选。精确提及高亮也保留实体身份，可再次点击打开对应条目。此交互只改变 Browser UI，没有新增 Host、Tools、SDK 能力或客户端桥。

全部识别成功时隐藏状态 banner；处理中、异常、尚未启动或刷新失败时在列表上方显示简短状态，已有实体始终可用。点击面板以外区域收起面板，保留同账号、同日期的筛选、展开和滚动状态。

## 历史验证记录

以下按开发阶段记录，不代表同步最新 dev 后的最终测试结果；最终提交对应的完整测试和制品验收结果见 PR 描述。

- 单测覆盖原文清理和 Unicode 位置、临时结果转正式结果、引用篡改、跨账号、请求中的账号/版本变化、来源不匹配、增量保留/清空、容量、分页和取消。UI 覆盖切账号/日期丢弃迟到结果、关闭与后台暂停轮询、失败退避、筛选、窗口化和高亮重叠。
- `NODE_OPTIONS=--no-experimental-webstorage pnpm test --maxWorkers=4`：950 个文件通过、13 个文件跳过；11130 项通过、18 项跳过。Node 25 自带的 localStorage 与 jsdom 冲突，使用运行参数关闭 Node 的 Web Storage；不修改源码或跳过失败测试。并发运行首次出现的引导时序和计时超时，在独立复验与低并发全量运行中通过。
- `pnpm typecheck` 通过；`pnpm pack` 的 prepare 完整构建通过，保留原包版本。产物清单无本机路径、临时证书、node_modules 或验收文件。
- 使用未修改的官方 DSH `dsh-v0.1.5-rc.2`（`fb2c4b9e698e30edb738bca4cf0618587db7d203`），经官方 `dsh plugin --profile web add <tgz>` 安装到全新临时 Home/Profile。路径包含空格，端口由系统分配。
- `tests/e2e/recording-entities.e2e.mjs` 通过：真实会话发现并调用 list/occurrences；仓外 Consumer 仅使用包的 `/sdk` 公开导出，编译并经真实 Host 调用；Chrome 中检查宽屏侧栏实际宽度、窄屏抽屉、类型筛选、提及读取、精确高亮、Escape/焦点恢复、内容失效和切日空态。UI 使用组件内样式，避免依赖 DSH 自动装载额外 CSS。
- 验收产物和截图保存在工作区的 `.codex-artifacts/recording-entities/`，不进入发布包。初次集成验收制品 SHA-256：`479fec847f2337a9ecf4516f91b7678992c32842f0fe74851ae36cb145aad7b4`，不包含后续交互迭代；后续真实客户端验收加载任务 worktree 最新构建。

初次页面、工具和 SDK 验收经过真实插件/DSH 链路，远端 Auth、Audio 和 Relation 使用隔离 HTTPS 夹具。随后使用最新客户端 master 的独立 Electron 开发实例，连接真实测试服：账号 3016，2026-08-12 系统转写返回 193 个实体，已验证“国兵”在 00:06:43 的原文跳转与高亮；备用日期 2025-12-30 有 46 个实体。没有触发识别任务、生产写入、发布或替换其他常驻实例；Windows/Linux 与已安装发行版客户端仍未验收。

### 交互反馈复验

- 标签图标位于转写对比右侧，工具栏不再展示实体文字和数量；图标提示与无障碍名称保留。
- macOS、同一独立客户端和测试账号 3016、2026-08-12、同一列表前三个实体，测量点击到提及按钮可见。修改前为 5349 / 4853 / 2250 ms，修改后为 854 / 539 / 950 ms；测量时全量测试已结束。两侧均为真实远端读取，不使用提及结果缓存。这是三次交互的实测样本，不作为延迟分位数或跨环境保证。
- 修改后 8 个相关测试文件、144 项通过，类型检查和构建通过；新增并行读取、失败取消、最终版本校验、精确原文来源及不加载说话人资料的回归检查。
- 全量首次运行：946 个文件、11124 项通过，4 个文件的 9 项失败，13 个文件 / 18 项跳过。失败涉及导入异步状态、聊天转发等待、发布脚本 5 秒超时和首页引导定时等待；随后导入、聊天、发布脚本三个文件（478 项）通过，首页引导再次独立运行 40 项通过。首页引导及其直接 owner 与 official/dev 一致，没有为通过测试修改引导代码或放宽等待。全量首次并非全绿，失败与复验日志均保留在 `.codex-artifacts/recording-entities/interaction-*.log`。

### 默认下划线复验

- 用户确认采用当天实体名称标注同词的插件内方案。5 个相关测试文件、41 项通过，覆盖重复名称、重叠/相邻范围、Unicode、字面匹配、词边界、未选中/选中样式和搜索叠加；类型检查和构建通过。本轮只修改 UI 展示，未重复全量测试。
- 独立 Electron 客户端、真实测试账号 3016、2026-08-12：未打开面板和点击实体前，711 行转写已有 266 段名称下划线、0 处选中高亮、0 次提及请求。点击“国兵”提及后，266 段下划线保留，精确位置增加蓝色底框。搜索叠加保持全部转写文本不变；切到其他日期隐藏该日选中位置，返回原日期保持既有定位状态。
- 已检查默认与选中状态截图。客户端仍使用隔离 App Data 和原测试账号，验收脚本、结果及截图位于客户端任务 worktree 的 `.codex-artifacts/recording-entities-run/`，本轮构建及测试日志位于插件的 `.codex-artifacts/recording-entities/arkme-entities-underlines-*.log`。

重跑页面验收时，准备未修改且已构建的目标 DSH checkout、新 Profile 中经官方 CLI 安装的 tgz、仅用于本地 HTTPS 的 CA/私钥，并设置 `ARKME_DSH_CHECKOUT`、`ARKME_PACKED_PROFILE`、`NODE_EXTRA_CA_CERTS`、`ARKME_E2E_TLS_KEY` 和可选 `ARKME_E2E_ARTIFACTS`。从 DSH checkout 运行 `pnpm exec vitest run --config <插件仓>/vitest.recording-entities-e2e.config.mts`。测试通过官方测试 scaffold 挂载已安装包，未修改 DSH 或引用插件私有源码。

### 图标与文字点击复验

- 5 个相关测试文件、46 项通过，类型检查和构建通过；滚动位置调整后再次运行 UI 文件，10 项通过。新增跨页定位、重复打开同一实体、筛选重置、鼠标与键盘操作、焦点恢复、旧提及取消和卸载取消的检查。本轮未重复全量测试。
- 真实账号 3016、2026-08-12：点击转写中的 `carry`（实体列表第 46 项），面板自动跳到第 2/7 页并将它滚到上方展开，仅发出一次提及请求，实体 DOM 保持 30 项。关闭后焦点回到点击处，Enter 可再次打开；点击 `国兵` 后选择提及仍精确高亮原文，再点选中词语可重新打开该实体。两种页面截图均已检查，Graph 图标与选定候选的路径一致。
- 相同真实数据（193 个实体、711 行转写）验证标注字符覆盖与修改前一致。仅名称范围计算的 Node 25 / macOS 小样本测量：修改前 10 次为 1.90–4.32 ms，修改后 2.97–7.84 ms；不代表页面渲染或远端请求耗时，也不作性能提升承诺。匹配器按当天数据准备一次，每段转写临时内存随文本长度有界，重复重叠命中先合并再写入身份。
- 运行证据保存在插件 `.codex-artifacts/recording-entities/entity-click-matching-profile.json` 和客户端 `.codex-artifacts/recording-entities-run/entity-click-acceptance.json`；客户端继续加载当前任务的构建，未发布制品。

### 识别进度 banner

实体列表上方以轻量 banner 展示后端当前阶段：排队、准备识别、等待转写、识别实体、核对实体、保存结果或恢复识别。排队不因已有转写而显示为正在识别；未知阶段仅显示“正在处理实体”。仅识别/核对阶段显示后端提供的完成单元数，不把等待转写时的计数当作整体进度；接口尚未提供阶段起始时间，UI 不生成估算耗时。状态更新、读取刷新、网络失败或自动更新暂停均保留已有实体列表和已展开的提及，实体仍可点击和精确定位。

本次仅修改 Browser UI 展示和刷新交互，复用已有 Host 的 stage/status/count 数据和既有轮询 owner；Tools、SDK、Host API 及客户端原生桥均无新增能力。新增阶段映射、未知阶段退化和状态变化期间列表/提及可用性的回归检查；专项测试 26 项通过（包含提及失败后刷新重试且保持展开）。

真实测试服账号 3016、2026-09-07 在 `waiting_more_transcript` 时显示“等待转写完成”与 53 个实体，展开提及、刷新保持展开及精确原文定位均通过；2026-10-13 首批三段录音的默认下划线、打开对应实体、提及定位、关闭与焦点恢复通过。类型检查与完整构建通过。本次全量回归单独记录其结果，不以 UI 验收替代全量门禁。

本轮整仓回归首轮 11135 项通过、18 项失败、18 项跳过，失败以超时为主。12 个失败文件单并发复验后仅首页引导 2 项未过；该文件再独立运行 40 项全部通过。失败文件未改动、未调整超时，保留首轮非全绿事实。最后补充的提及失败手动刷新路径经 26 项专项测试、类型检查、界面构建和真实客户端一次受控读取失败后的恢复验证通过。

### 面板状态与连续滚动

当前交互采用通用 ArkmeDetailShell、ArkmeRightPanelHeader 和宽度调整机制，关闭按钮统一使用 Phosphor X，并避让 macOS 顶部拖动区域。详情以满屏高度固定在窗口右侧，保持正文宽度；拖动左侧边框可调整宽度，也支持方向键、Home/End 和双击恢复默认宽度。仅面板宽度作为无业务数据的本地偏好保存，使用独立 preference key。

当前账号/日期持有唯一面板实例：首次打开后关闭只隐藏，保留类别、展开项、滚动 DOM 和块高度；切换账号/日期或卸载录音页销毁该状态，不跨账号保留实体。关闭取消提及请求，释放滚动观察器、宽度观察器和 Escape/focus 所有权。重新打开同版本已完成的提及不重复读取；版本变化丢弃旧提及，迟到响应不会写回。失败时仍展示先前读取的提及，滚动加载停止并提供手动重试。

实体列表不再有分页按钮。提及也连续滚动，单请求、每批 50 条、最多保留当前一个实体且受 Host 的 10000 条上限约束；无进展或越界页显式失败。使用通用目录窗口限制已挂载条目；不支持 IntersectionObserver 的环境使用可点击的加载更多入口。此轮没有新增 Host 业务能力，Tools/SDK/客户端桥无需扩展，仍复用既有接口。

本轮验证：54 项相关测试通过；整仓首轮 950 个文件通过、1 个文件失败、13 个文件跳过（11156 项通过、2 项失败、18 项跳过），失败均在未修改的首页引导测试。该文件与本轮相关测试单并发复验共 94 项全部通过，未放宽超时或跳过用例。类型检查、完整构建、打包清单检查通过；新制品通过官方 CLI 安装至隔离 Profile，运行未修改 DSH 的 Tools/SDK/宽窄页面 E2E 通过。

真实测试服账号 3016、2026-08-12 当前有 225 个实体、30 个人物：关闭重开和提及定位返回后，“人物”筛选、展开项和 650px 滚动位置保持一致，重开未新增提及请求；实际拖动面板从 405px 到 453px，并保持宽度。连续滚到最后一个实体“斧王”时只挂载 45 个实体条目；点击转写中的 carry 自动滚到相应条目并展开。以上为 macOS 独立 Electron 开发客户端验收，未发布插件或修改客户端/DSH 源码。

### 真实转写状态与手动识别

banner 将 waiting_transcript 显示为「等待转写完成」，waiting_more_transcript 显示为「等待剩余转写完成」。后端报告转写失败或所需处理服务不可用时显示转写异常；已有实体时显示「部分转写异常，已有实体可查看」，不会隐藏列表或取消已展开提及。健康摘要独立于投影版本更新，旧后端可降级读取 transcript_progress 的失败数；不根据浏览器已等待时间推断失败。

not_started 且后端 can_start 允许时，banner 提供「开始识别」。提交期间按钮禁用，30 秒截止；切账号、日期或离开录音页取消请求并丢弃迟到响应。响应未知时仅刷新状态，绝不自动重放写请求。刷新按钮始终只读，不触发识别。

Host `recordings.entities.start` 要求当前允许的 Origin，使用统一写请求通道，输出仅包含 accepted/processing/succeeded，不泄露 job_id 或内部错误。Tools 新增 `arkme_recording_entities_start`（explicit-user-write）；公开 SDK 新增 `recordingEntityStart` 能力发现和 startRecordingEntities，旧 Provider 明确报不支持。三者共用 RecordingEntityService 和 Relation 幂等 ensure，客户端没有新增原生桥。

本次验证（2026-10-14）：最终专项 120 项、Host/写入口权限 126 项通过，类型检查与构建通过。完整回归首轮 11166 项通过、18 项跳过、2 项失败；发现的 Origin 校验缺失和 facade 合同清单漏项均已修复，并在上述专项中复验通过，保留首轮非全绿记录。最终不可变包经官方 CLI 安装、未修改 DSH 的真实会话 Tools、仓外 SDK、宽窄 UI、旧录音开始识别及异常恢复端到端通过，测试执行 37.82 秒，未放宽 120 秒测试超时。

测试服 Audio #508 与 Relation #450 部署成功。15:44 的真实账号 3016 / 2026-09-07 页面有 39 个实体、2 个已完成转写单元、1 个仍处理且所需服务不可用的单元；banner 显示「部分转写异常，已有实体可查看」，提及定位、刷新保留实体、关闭重开保留类别/展开/滚动通过。该日已有任务，不作为未启动旧录音样本；后者由隔离端到端覆盖。仅在独立 macOS 客户端加载本次插件验收，未发布插件公共渠道，未修改客户端或 DSH 源码。


## 仅手动识别

录音上传、转写完成、打开页面和读取实体均不会启动识别或扣除实体识别积分。选择录音日期，打开「实体」，点击「开始识别」；失败时可以「重试识别」。识别使用账户 AI 积分，提交成功后可关闭面板，任务仍会执行，重新打开可查看进度。录音内容更新后需要再次手动触发；没有变化的已完成任务复用结果。

DSH 会话可说“识别 2026 年 10 月 10 日录音中的实体”。模型先通过录音日历确定该日期的 owner `date_stamp`，仅在用户明确要求时调用 `arkme_recording_entities_start`，再通过只读 `arkme_recording_entities` 查询进度或结果。`accepted` 表示已提交，不能描述为完成；不得自动批量扩展日期、定时调用或在读取失败时启动识别。

Tools、UI 和公开 SDK `startRecordingEntities` 共用 `RecordingEntityService`。服务请求 Relation `/api/v1/entity/audio-day/start`，不回退到旧 `/ensure`，不缓存或自动重放写请求。旧 `/ensure` 只返回 `manual_start_required`，不创建或授权任务。后台只执行新版手动授权的任务，旧自动积压不自动恢复，已发布结果保留。


## 识别类型与质量合同（2026-10-15）

type 8 统一表示「品牌」，与 Relation 实体枚举一致；金额不是品牌。明确金额及单位使用 type 9 的「金额」类别，保留不同数值为不同事实，不能合为「价格金额」。人物、组织、地点、宠物、事件、项目、作品和其他的编码不变。UI、Tools、SDK 展示后端的相同类型，不自行重分类或按名字归并。

后端识别以原文中的可识别对象为准，代词、裸数字、时长、泛化动作和无意义残片不独立创建实体。带数字的有效名称、单字姓氏、命名的游戏角色与装备仍可识别。音近、同类别、同后缀和同录音不能单独证明两个名称为同一对象；证据不足时保留原文名称和独立结果，允许用户反馈。

后端新的模型协议、候选准入和本地检查不改变 Host API 或三端授权方式，仍由用户按日期明确开始。升级不会自动重跑历史录音，也不会自动删除历史实体。费用以账户 AI 额度中的「录音实体识别」实际账本为准；测试用隔离模型评测费用另列，不把实验调用数或任务中间计数当作正式日期账单。
