Files
trade-message-center/.trellis/tasks/09-15-onetalk-rendered-card-readers/prd.md
T

9.3 KiB
Raw Blame History

统一修复 OneTalk React 卡片观察

Goal

让已挂载的 OneTalk 订单、询盘与商品卡片均能由 MAIN world 的 React Fiber 白名单读取器产生受控的 rendered-card observation,并沿既有 bridge、durable ledger、Bright 补全事实与 Mind message.updated 链路入库和读取。基础消息事实及其同步语义保持不变。

Confirmed facts

  • 已有 observer 仅接受订单 guardmsgType=10010originalData.cardType=9),因此询盘和商品在归一化前被拒绝。
  • 真实订单通过 identity、卡片类型和模板谓词;其 productInfoList/productList 项实际使用 productNameproductImage。现有共享商品投影仅接受 titleproductTitlename,导致订单整体 observation 为 null,本地 rendered-card ledger 没有记录。
  • 真实询盘使用 msgType=10010originalData.cardType=6。其 Fiber memoizedProps.datainquiryCardDTOdisplayProducts/productsinquiryContentinquiryID;商品项使用 productNameproductImageproductMOQproductUnit。该样本中 inquiryContentrichTextContentinquiryCardDTO.summary 相等;可见标签来自 detailStrquoteStractionName
  • 14 条真实 msgType=101 消息中,仅一条在 Fiber 子树有 props.cardType=54,因此商品必须同时满足两层 guard。该模板的 props.datatitleproductImagepricemoqmoqUnitproductActionbenefitFirstTextbenefitSecondTextpromotionTag
  • 商品 productAction.actionParams.url 是无凭据、无端口、无 query/fragment 的 https://chinese.alibaba.com/product-detail/...;其路径产品 ID 与 hsfId 一致,可经现有 normalizeOneTalkProductUrl 产生安全的 sourceUrlproductId
  • 当前 rendered_product.product.sourceUrl 错误地使用卡片图片 URL 白名单校验;该白名单不含 chinese.alibaba.com,与 normalizeOneTalkProductUrl 的唯一允许结果不相容。因此在启用商品 reader 前,contract 必须改为以 canonical product URL 规则验证 sourceUrlproductId
  • 商品 productImagehsfImg 都通过现有图片 URL 白名单,但本次样本二者相同,无法将 hsfImg 证明为独立的店铺图。两个值均在 Fiber 子树的 image URL props 中传递;实际 DOM 图片已被渲染器转换,无法作同值关联。用户明确决定商品只保留商品图,不保留或构造店铺图。
  • 既有专用 bridge、rendered-card ledger、rendered.card.observed/ack、服务端补全表和读取投影已经处理受控 OneTalkRenderedCardContent。本 task 不改变它们的身份、ACK、冲突、授权、发布或数据库语义。
  • 已确认 6be3b839 新增 rendered-card ledger 的 listPending() 后,61df41d 的 IndexedDB 索引重构删除了通用 readAll(),却没有迁移该 consumer,导致 Chrome extension TypeScript check 在未定义的 readAll 处失败。用户已授权把这个阻塞本 task 验证的缺口作为最小前置修复。

Requirements

R1. One dispatcher, three strict readers

  • 保留 MutationObserver 仅作为 .message-item-wrapper 挂载触发器,且保留现有 messageId + conversationCode + channelAccountId 身份、会话前后不变和 base evidence 校验。
  • 将当前订单专用 reader 改为按明确 classifier 分派的三个 typed reader;不得泛化地递归读取或桥接完整 props。
  • 订单仅接受 msgType=10010originalData.cardType=9,并要求已验证的订单模板谓词。
  • 询盘仅接受 msgType=10010originalData.cardType=6,并要求已验证的询盘模板谓词。
  • 商品仅接受 msgType=101 且同一受限 Fiber 子树存在 renderer cardType=54,并要求已验证的商品模板谓词;其余 msgType=101 必须继续忽略。
  • 未知卡、文件卡、身份不完整、会话切换、缺失模板或任一白名单字段不满足时 fail closed,且不退回 DOM 文案、节点顺序、CSS class 或 raw originalData.params 推断。

R2. Exact white-listed projections

  • 订单商品投影支持已验证的 productNameproductImage,并保留现有兼容字段;订单其余内容保持既有受控 contract。
  • 询盘投影为 rendered_inquiry:商品名/图片、productMOQ + productUnitinquiryContentinquiryID 和仅标签/可用性的动作信息。不得跨出 action params、callback、URL、原始 DTO 或原始 content。
  • 商品投影为 rendered_producttitleproductImagepricemoq + moqUnit、经 normalizeOneTalkProductUrl 清洗的 productAction.actionParams.url,以及仅当前可见且非空的服务/促销文案。storeImageUrl 必须从 shared contract、校验、fingerprint、reader、bridge payload、持久化 JSON 和测试样例中完全删除。不得桥接 traceInfo、action params、action list 或产品原始对象。
  • rendered_product.product.sourceUrl 必须由 product URL canonicalizer 验证,并与 productId 对应;不得使用图片 URL validator 代替产品引用 validator。
  • 每个 reader 必须依赖共享 contract 的文本、图片 URL、产品 URL、fingerprint 与 equality 规则;不得自行复制安全校验或构造不安全 URL。

R3. Preserve the established downstream boundary

  • 三类 observation 统一走已有 page bridge、Service Worker coordinator、独立 ledger、Bright ACK 和 server supplement 流程;不得以新消息、重跑基础同步或直接写数据库的方式绕过它。
  • 基础 content、消息/会话时间字段、checkpoint、history cursor、message.created 和重复/冲突语义不得改变。

R4. Regression and runtime verification

  • 为订单、询盘、商品各添加 reader fixture;覆盖真实字段名、strict classifier、模板缺失、身份/会话失配、文件/未知卡及非商品 msgType=101
  • 订单 regression 必须证明 productName 不再使整张卡拒绝;询盘和商品 regression 必须证明只生成各自的 exact contract shape、且不含原始或可执行字段。
  • 保留并扩展 observer/bridge/coordinator tests,证明 observation 仍先写 ledger,且只在基础 candidate 已确认后发送。
  • 在真实 Chrome 页面无点击、无刷新地验证三类 card reader 只产生规范化 observation;数据库验证必须在对应基础消息存在、连接授权且 ACK 成功的边界下单独报告。

R5. Repair the rendered-card ledger's indexed pending query

  • listPending 收窄为必填 channelAccountId,并只读取该账号的 pending_ack rendered-card ledger records;不得恢复全 store getAll() 或在内存过滤所有账号。
  • 以新 IndexedDB version 只为 onetalk_rendered_card_ledger 新增 [channelAccountId, status][channelAccountId, conversationId] indexes。现有 records 必须保留,不能清库、重键、回填或改变 ACK/first-content-wins 行为。
  • clearConversationHistory 必须在其既有 readwrite transaction 中通过 [channelAccountId, conversationId] 删除同一会话的 rendered-card records;这恢复其已有测试和 history rebuild 语义,不扩大清理范围。
  • 为由旧 v9 数据库升级的 record 保留、pending scope 查询和 conversation cleanup 添加回归;此 scope 只修复 index refactor 的遗漏,不改变 bridge、ledger 状态机、wire、服务端或业务卡 reader 行为。

Out of scope

  • 不改变 rendered-card wire version、bridge envelope、服务端 schema/migration、授权、ACK/冲突状态机或 Mind 事件语义。唯一例外是 R5 的 IndexedDB v10 index-only 升级。
  • 不主动点击、翻页、导航、请求额外 OneTalk 接口或改写 React/OneTalk 页面状态。
  • 不采集文件、图片消息、文本消息、名片、未知卡或任何未白名单的 Fiber/DOM 字段。
  • 不将订单状态变化视为可覆盖的补全更新,也不将卡片动作变为可执行命令。

Acceptance criteria

  • 已挂载的真实订单可产生 rendered_order observation,并且实际 productName 作为商品标题进入受控 contract。
  • 满足 cardType=6 的询盘可产生完整、受控的 rendered_inquiry observation;文件卡和非询盘 msgType=10010 不产生 observation。
  • 仅同时满足 msgType=101 和 renderer cardType=54 的商品可产生 rendered_product observation;其他 msgType=101 保持忽略。
  • 三类 projection 均通过 contract 验证、没有 raw content/DTO/params/callback/trace 信息、隐藏 ID 或未经批准 URL;rendered_product 的任何路径均不包含 storeImageUrl
  • canonical 的 chinese.alibaba.com/product-detail/... 商品引用通过 rendered-product contract;图片 URL、带 credential/fragment 的 URL、非 canonical product path 或与 URL 不一致的 productId 均被拒绝。
  • 订单、询盘、商品的 targeted reader/observer/coordinator tests 通过,且现有 rendered-card、基础同步和历史读取回归不变。
  • 真实 Chrome 只读探针确认三类卡各自命中正确 classifier 和模板谓词;端到端入库仅在不触发主动同步的前提下、已有基础消息确认时验证。

Decisions

  • 商品只保存 product.imageUrlstoreImageUrl 没有业务含义或运行时证据,必须完全删除;不得以 nullhsfImg 或任何替代字段保留该概念。