Files
trade-message-center/.trellis/tasks/09-11-remove-image-dimensions/research/contract-extension.md
T

16 KiB
Raw Blame History

研究:共享 OneTalk 图片合同与 Chrome Extension MAIN 链路

  • 查询:核对 OneTalk 图片 canonical type/guard、MAIN raw/flat-history 解码、上传 metadata、live sent correlator、页面观察发布顺序、v5 协议常量消费者、现有测试与文档中的 width/height 读写点。
  • 范围:internal(仓库源码、测试、规格、GitNexus);运行态未执行。
  • 日期:2026-09-11
  • 协调类别:cross-cutting
  • 阻塞:共享合同/协议版本 scope 必须先稳定;Chrome extension MAIN scope 才能独立收敛。extension scope 还依赖 server/harness 接受同一个 v6 frame/content 形状。
  • 共享边界:apps/onetalk-contract/src/content.ts 是 image canonical type/strict guard 唯一 ownerapps/onetalk-contract/src/wire.ts 是 wire version ownerapps/chrome-extension/src/onetalk/main-page/message-observer/content-decoder.ts 是 raw OneTalk 到 normalized content 的唯一 MAIN decodersend-observation.ts 是 live/send correlator owner。
  • 证据基线:checkout /Users/ybf/code/trade-message-center-worktreebranch mainHEAD b47720872da147e9da791de77424007e21e1a74c2026-09-11);产品源码/测试无 dirty path,只有本任务 .trellis/tasks/09-11-remove-image-dimensions/ 未跟踪。GitNexus 当前索引同一 HEADindexed 2026-09-11T02:51:38Z
  • 复用证据与缺口:复用本任务 prd.mddesign.mdimplement.md 的 v6 严格切换和 live-confirmation 约束,以及 .trellis/spec/chrome-extension/frontend/onetalk/{page-bridge,send-sop,runtime-sync}.mdGitNexus 对 normalizeImagemetadataFromcreateSendObservationCorrelator 的 upstream 影响均为 LOW,但索引对部分 const/type 只给出符号级关系,仍以源码搜索为准。未执行 Chromium、Bright/Mind server、PostgreSQL 或外部 trade-mind runtime 验证。

发现

1. 当前实际数据流与 owner

OneTalk raw contentType=101/custom.type=7
  -> decodeMediaPayload(Base64 -> UTF-8 -> JSON)
  -> normalizeImage (MAIN only)
  -> observedMessage (shared isOneTalkMessage guard)
  -> WebSocket observer sink / page-script-entry
  -> sendObservation.observe(batch.messages) [先于 observedSink]
  -> Service Worker page bridge -> Bright send.confirmation
  • apps/chrome-extension/src/onetalk/main-page/message-observer/content-decoder.ts:175-210 当前将 payload.widthpayload.height 写入 normalized image,并用 ONETALK_MAX_IMAGE_DIMENSION_PX 校验;因此缺失、错误类型或超界的 raw dimension 会生成 media_invalid_schemadecodeOneTalkRawContent:289-302 和 flat-history 分支 265-286 都汇入同一个 normalizeImage,不能另建 parser。
  • apps/chrome-extension/src/onetalk/main-page/message-observer/model.ts:148-247 先校验身份,再调用唯一 content decoder;只有完整 isOneTalkMessage 才生成带 upstreamType: 1 的观察消息。该边界只输出 normalized content,不能把 raw custom.data 或 raw dimensions 带出 MAIN。
  • live 入口是 new.ts:12-51index.ts:9-32websocket.ts:40-97raw history WebSocket envelope 在 index.ts:24-31 被忽略,历史由 SDK adapter history.ts:71-143 使用同一 content decoder。page-script-entry.ts:38-46,56-90 在发布 Bright sink 前先调用 correlator,故 live sent batch 可以直接完成 pending send。
  • apps/chrome-extension/src/onetalk/main-page/message-observer/send-observation.ts:24-41,115-182 的 image pending expected 当前为 sizeBytes, md5, 可选 width, height, fileIdmatchesImage:119-125 读取并比较两项 dimensions。matches:162-181 保留既有顺序:目标会话 + sent + 完整消息;有 candidate ID 时只按 ID(不回退指纹/时间),无 ID 时才使用 sentAfterMsnow + 5_000ms 的窗口及 image fingerprint。
  • send-observation.ts:184-376createSendObservationCorrelator 仍是唯一 pending/终态 owner;图片预算由 ONETALK_IMAGE_SEND_TIMEOUT_MS:11 保持 45 秒,finish 时只允许完整 sent message 变成 confirmed_sent,多 pending 匹配是 send_ambiguous,错误/超时继续 delivery_unknown
  • apps/chrome-extension/src/onetalk/main-page/image-send.ts:21-29,185-217FinalImageMetadatametadataFrom 读取 width/height 并将其可选写入 expected metadatafinalMetadataFromSendFileArgs:240-255 从直接参数及 mediaInfo/relationInfo/fileInfo 收集候选。installUploaderInterceptor:304-385 在匹配 tmpKey 后先做 metadata/current-target fence,再调用 correlatorsendOneTalkMedia:408-592 在 fetch/blob/File 后、native sendFile 前注册短生命周期 pending。生产 relation callback 的无 dimensions 形状已经在 image-send.test.js:148-155 注释和 relationMetadata:98-103 fixture 中存在,当前缺口主要是 MAIN decoder/contract 强制 live payload 带 dimensions。

2. 共享合同与公共读边界

  • apps/onetalk-contract/src/content.ts:5-30 定义 ONETALK_CONTENT_VERSION=1、媒体大小上限和 OneTalkImageContentCONTENT_KEYS.image:82-96isOneTalkImageContent:282-296 同时决定 exact-shape accepted keys。移除 dimensions 时必须同时删除 type、key list、dimension constant 和 guard reads;保留 fileId/extension/sizeBytes/isOriginal/md5/previewUrl/urlScope 与现有 media size/URL allowlist。
  • apps/onetalk-contract/src/messages.ts:94-121isValidOneTalkMessagePayload 通过 isOneTalkMessageContent 验证 observed framesapps/onetalk-contract/src/sending.ts:140-166 的 confirmed send guard 通过 isOneTalkMessage/isOneTalkCenterMessage 验证完整 sent message。因此没有另一份 message/send image schema 可安全绕过共享 guard。
  • apps/server/src/onetalk/read-projection.ts:84-110 只对持久化 content 再调用共享 isOneTalkMessageContent 并浅拷贝,server source 没有独立 dimensions reader;但 server image fixtures 仍在 apps/server/test/onetalk-read-domain.test.ts:395-420,478-518,由 server scope 清理后才能读取无宽高历史。
  • Page bridge contract 已规定 MAIN decoder 是唯一 raw boundary、ISOLATED/SW 不重解析 custom.data.trellis/spec/chrome-extension/frontend/onetalk/page-bridge.md:64-70,111-115,193。send SOP 已规定候选 ID 优先、完整 sent fact、45 秒媒体观察和三态 fail-closed.trellis/spec/chrome-extension/frontend/onetalk/send-sop.md:37-43,67-78

3. width/height 全量业务读写点(当前 checkout)

  • Canonical shared writer/readerapps/onetalk-contract/src/content.ts:7,18-30,82-96,282-296
  • MAIN raw/flat-history normalized writer/readerapps/chrome-extension/src/onetalk/main-page/message-observer/content-decoder.ts:6,187-210
  • MAIN upload metadata writer/readerapps/chrome-extension/src/onetalk/main-page/image-send.ts:21-29,185-217;不是 UI geometry。
  • MAIN live correlator expected/matcher readerapps/chrome-extension/src/onetalk/main-page/message-observer/send-observation.ts:24-35,115-125
  • Extension test canonical/raw fixturesapps/chrome-extension/test/onetalk-media-content-decoder.test.js:19-28,54-70,107-136,157-191send correlator fixture onetalk-send-observation.test.js:169-224image upload/callback fixture onetalk-image-send.test.js:61-96flat-history fixture onetalk-websocket-tap.test.js:355-371。这些应改成 canonical 无宽高,并新增 raw dimension absent/one-sided/wrong/over-bound/present cases均产出相同无宽高 canonical content;若保留 raw evidence 字段,必须只出现在明确标注的 input fixture 中而不出现在 expected/public output。
  • Harness/server 是 R1 之外的共享下游:apps/mind-test-harness/src/harness/validators.ts:18-30messages.ts:17-35 仍验证/显示 dimensionsapps/server/test/onetalk-read-domain.test.ts 的 image expected 仍带 dimensions。CSS/layout geometry(例如 apps/chrome-extension/src/onetalk/main-page/conversation-id-copy.ts:27-36action-status-tooltip.ts:47-100、harness styles.ts)不是消息字段,不应被全局文本替换。
  • 当前源码未发现其他 extension page-bridge、Service Worker、Bright client 或 database schema 对 image dimensions 的业务读写;Service Worker 只传递已收窄消息。apps/chrome-extension/src/onetalk/service-worker/flows/frame-writers.ts:145-176 直接写 message frame,但 payload 类型来自 shared contract。

4. 协议版本消费者与升级影响

  • owner/decoderapps/onetalk-contract/src/wire.ts:41,123-135 定义 v5 常量和 frame 类型;decoder.ts:177-197 对非当前版本返回 onetalk_protocol_upgrade_required,不能静默剥离旧 image fields。
  • shared frame creators 全部导入该常量:connection.ts:3,185-221conversation-sync.ts:10,271-351messages.ts:10,177-220contact-profiles.ts:4,165-176buyer-facts.ts:4,401-416sending.ts:8,173-204index.ts:39-53 对外导出。把常量升至 6 会统一改变所有 frame builders,不应只改 send/image frame。
  • extension exact consumersservice-worker/transport/bright-client.ts:3-12,232-260 生成 plugin hello/heartbeat 并调用 shared decoderservice-worker/flows/frame-writers.ts:3-24,145-210 手工生成 observed/sync framesbright-client.test.jsbright-protocol.test.js 使用 shared constant 验证 handshake/upgrade。
  • server admission consumersapps/server/src/cutover-policy.ts:3,11-43websocket/handler.ts:197-271websocket/index.ts:70-87websocket/connection-store.ts:123-126http/onetalk/public-context.ts:136-170http/onetalk/summary.ts:46-55。这些都要求 current ONETALK_PROTOCOL_VERSIONcutover-policy 的 mode 字符串仍叫 bright-v513,26,39),它是独立 release-mode label,是否改名为 v6 需由 cross-cutting owner 明确,不能误以为只改数字常量即可。
  • hard-coded test consumersapps/server/test/observation-batcher.test.ts:53-71 直接写 protocolVersion: 5apps/server/test/mind-authorization.test.ts:359-369 直接以 canAdmit("bright-v5", ..., 5) 验证。apps/onetalk-contract/test/contract.test.ts:191-219,1295-1303 和 extension/harness tests 多数引用 shared constant,但测试名称/文档仍写 v5。
  • harness consumersapps/mind-test-harness/src/harness/contract.ts:3-36 从 shared contract 注入 protocolVersionharness/websocket.ts:48-174 使用注入值发送 hello/heartbeat/send;这与 image validator 是同一个 downstream release participant。

GitNexus pre-change evidencenormalizeImage upstream 4 symbols / 2 observer processes / LOWmetadataFrom upstream 3 / sendOneTalkMedia process / LOWcreateSendObservationCorrelator upstream 1 (page-script-entry.ts) / LOW。它们支持 extension module 边界,但 shared contract/protocol 的实际 fan-out以源码 imports 和 frame builders为准。

候选 Scope 与依赖

  1. Shared v6 image contractcross-cutting prerequisiteowner apps/onetalk-contract/src/content.tswire.ts、相关 contract tests;交付无宽高 exact-shape image、v6 decoder/builders、旧 v5 明确 upgrade error。应排除 extension implementation、server migration 和 harness UI。所有消费 shared content/frame 的 writer 必须等待此 scope;旧 v5 client 不可兼容运行。
  2. Chrome MAIN image observation/send confirmation(本 R1 package-local implementation scope,依赖 1owner content-decoder.tsimage-send.tssend-observation.ts 及其 decoder/image-send/send-observation/websocket tests;交付 raw dimensions 完全忽略、无宽高 live sent 可确认、candidate ID 优先/唯一 fingerprint/时间窗/目标身份/45 秒终态不变。可排除 server persistence/read projection、harness、external Mind。该 scope 与 server migration、harness validator 的源码写入可 write-disjoint,但由于同一 shared contract 和 v6 wire prerequisite,不能在 scope 1 未稳定前开始;合同稳定后才可并行。
  3. 明确不属于 R1 的下游server read projection/JSONB migration 和 harness validation/rendering 需要消费新的 canonical shape;不能由 MAIN scope 通过保留 dimensions 或兼容 parser 解耦。

Invariant 与验收探针

Static/unit probesimplement/check 可执行)

  • rg -n -i --glob '!**/dist/**' --glob '!**/node_modules/**' '\b(width|height)\b|ONETALK_MAX_IMAGE_DIMENSION_PX' apps/onetalk-contract/src apps/chrome-extension/src/onetalk/main-page apps/chrome-extension/test/onetalk-{media-content-decoder,image-send,send-observation,websocket-tap}.test.js:预期无 canonical/metadata/correlator业务读写;仅允许明确 raw input fixture,不能出现在 expected normalized/public content;区分 CSS geometry。
  • pnpm --filter @trade-message-center/onetalk-contract testcontract guard 断言无宽高 image accepted、任一额外 dimension key rejected、保留 size/URL/nullable metadata 边界,旧 v5 frame 返回 upgrade error。
  • pnpm --filter @trade-message-center/chrome-extension testdecoder 断言 raw image dimensions 缺失、单边、错误类型、超界、正常存在均得到同一个无宽高 normalized content,非法 size/URL 仍 anomalywebsocket/parse tests 断言 history/live 共用 decoder且 raw 不跨 bridge。
  • correlator/image tests:无 candidate ID 时仅 conversationId + sent + kind=image + sizeBytes + md5 + optional fileId + window + unique 可确认;candidate ID 仍可越过时间/指纹但不能越过完整 sent guardwrong conversation/direction、received、不完整、同图并发均不确认/send_ambiguousrelation metadata 中缺 dimensions 或带任意 dimensions 均不改变 expected。
  • pnpm typecheckpnpm buildpnpm format:checkgit diff --check;改协议后确认所有 frame creator/import 编译通过,并搜索 hard-coded protocolVersion: 5 / canAdmit(..., 5)

Runtime probes(研究期间未执行)

  • Chromium/CDP 隔离要求:使用无敏感测试图片、已重载 v6 extension 并重开 OneTalk tab;确认 MAIN/ISOLATED/SW build hash相同。当前已知浏览器规则要求先观察真实 page_command_result,不能把 SDK Promise、页面图片、WebSocket frame count 当成功证据。
  • 最小探针:唯一同账号页面、selected 会话可与目标不同;发送流程 fetch → File → tmpKey interceptor → native sendFile → live WebSocket sent observation。记录仅脱敏 stage/request reference;检查 live canonical content 不含 width/height,并确认同一次 pending 得到 confirmed_sent
  • 反例矩阵:raw payload dimension 缺失、仅 width、仅 height、字符串/负数/超界/随机 object 与正常 dimensions 各跑一次;其它 size 非法、URL host/action 非法仍产生原 anomaly。并发同图两 pending 必须 send_ambiguous,断线/目标账号变化/超时必须保留 delivery_unknown
  • v6 deployment probe(需维护窗口,不在研究中启动):停旧 server → 执行已审查 JSONB migration → 启动 v6 server → 发布并重连 v6 extension/Mind;旧 v5 hello/frames 应收到明确 onetalk_protocol_upgrade_required,不接受/剥离旧 dimensions。生产/真实 Mind 联调当前未验证。

注意事项 / 未找到内容

  • docs/onetalk-image-send-observation-feasibility.md:9-25,435-459,472-554,560-578,620-648 把 dimensions 写成 canonical image 和 correlator 条件;docs/onetalk-media-message-sync-prd.md:145-146,235-252 也有 image contract/code;这些当前文档必须同步为无宽高版本。docs/onetalk-media-message-format-investigation.md:152-210docs/onetalk-message-content-formats.md:301-363 同时包含已验证 raw payload 样本(应保留但明确 raw dimensions 在 MAIN 边界被忽略)和旧的推荐 normalized contract(应更新/标注)。
  • apps/server/src/onetalk/read-projection.tsservice.ts、repository/schema 没有独立 image dimension reader;它们依赖 shared guard/JSONB 通用拷贝,但历史 image JSONB 在 guard 更新后需要 migration 才可读取。R1 不改 server migration。
  • 与消息合同无关的 DOM/CSS width/height(例如 conversation-id-copy 的可见矩形)必须保留;全局删词会破坏页面控件测试。
  • ONETALK_PROTOCOL_VERSION 升级是全协议 strict cutover,不是 image-only adapter;共享常量变更会影响 auth/heartbeat/sync/profile/buyer/message/send 所有 frame。bright-v5 mode label 的处理是待 cross-cutting architecture decision。
  • 外部参考:无新增网络/外部文档;仓库内运行态调查仅作为历史证据,当前轮次未重新启动 Chromium、服务或数据库。