Skip to content

fix(lark): synchronize App images in the original conversation - #7348

Merged
huangruiteng merged 3 commits into
mainfrom
codex/original-conversation-image-sync-20261026
Oct 26, 2026
Merged

huangruiteng merged 3 commits into
mainfrom
codex/original-conversation-image-sync-20261026

Conversation

@loopx-agent

@loopx-agent loopx-agent commented Oct 25, 2026 •

Copy link
Copy Markdown
Collaborator

App-origin images currently become an “attachment not synchronized” placeholder in the original Lark conversation. This change sends the canonical caption and ordered inline images through the existing source-bound outbox, resource uploader and rich-post sender. Long captions place images on the last concrete post; independent byte readback is required before media completion.

Sends, edits and resumed parts recheck the original source and current Core grant after provider preview, before recording the write intent. Unknown upload acknowledgements and sends without a message locator stay unconfirmed across restart. Known attempts recover without another upload/send. Previously delivered placeholders retain their frozen receipts and partial status. No additional consumer, model task or scheduler is introduced.

Validation at deed999:

  • 352 affected HTTP/Core/SQLite, image, private-conversation, Markdown and result-file tests pass.
  • The target-main production snapshot reproduces the missing-image failure; late Core disconnect during short/long and resumed-part send preflight prevents subsequent writes at the fixed head.
  • Standard premerge: 6 direct and 11 selected checks pass, no manual holds; changed-file lint, compilation, bilingual RFC and public-boundary checks pass. CI is not consulted under the current review policy.

Provider/model test doubles qualify the source transport and existing real storage path. Live Lark rendering, installed service adoption, full history and reply-relation synchronization remain separate acceptance. Extra source verification incurs provider IO; no latency or cost improvement is claimed.

原 App 图片沿原私聊发送,并以原消息回执、独立资源字节读回和重启恢复确认结果。预检期间撤权会阻止后续发送;旧占位消息保留历史及 partial 状态,不自动重放。完整同步与安装态双端验收仍分开核验。

Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>

@loopx-agent loopx-agent left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewer: model_agent; gpt-6.1-sol; OpenAI; runtime_reported; effort=max

Request changes conclusion (author-owned PR; GitHub blocks formal self-review)

动机

在 App 继续原飞书私聊的用户,需要把图片及正文交还原会话。基础版本发送附件未同步占位说明;当前提交的正常路径会上传并发送有序图片,但在发送预检期间撤销连接后,仍会发送图片。正常路径的图片、正文、同会话身份及重启回执已有验证;撤权后零发送的验收尚未满足。本 PR 不增加模型任务、消费者或调度器,不负责完整历史、回复关系镜像和安装态双端采用。图片内容在用户撤权后继续发送,是本次新增媒体交付不能接受的结果;后续标为未确认并不能撤回已经发生的发送。

改动思路

资源 IO 复用既有 return_files,消息仍归原 Session/outbox,分段和回执仍归原 sender,放置合理;需要修复同一发送边界的最后授权检查,而非增设另一消费或状态 owner。当前 PR 的有用边界是原私聊中的有界图片交付及原回执恢复;该边界必须先补齐撤权后的发送阻断,安装态视觉与完整同步目标仍分开验收。沿现有 HTTP 输入、Core 会话、原私聊 outbox、资源上传和 rich-post 回执完成闭环,比新增桥接服务或模型任务合理。上传前的检查、未知上传确认的冻结和已知消息回执恢复都已有价值;问题在于发送预检是一次外部 IO,预检结束后不能继续沿用之前的来源授权快照。

具体改动

完整检查十条路径,基线 2b72d30fca7f452a71dffb723ebf6d1e2e878484,精确提交 3672381d1632a97c2f73315c6f3b790913759318。生产改动覆盖原 App outbox、资源 IO、短/长 rich-post 发送与验证、映射模板的请求大小计算;两个新增测试文件245行覆盖图片顺序、长正文末段、字节不符、未知确认、重启和旧占位消息;中英文 RFC 一起限定交付范围。既有 file 返回也经同一个资源 helper,不能只看新增图片用例。

规范依据 docs/architecture/rfcs/app-conversation-and-async-inbox-v0.md,固定版本 2b72d30fca7f452a71dffb723ebf6d1e2e878484。original-session-outbox:已实现,真实 HTTP/Core/SQLite 对照仍是一条原 Session、一条 App 输入,没有新模型工作。fresh-source-before-write:未满足,下述反例在实际 Core 撤权后仍发送图片。recover-known-attempt:已有正常回执/重启、字节不符及未知效果用例,已知尝试不再上传或重发,缺定位符保持未确认。bounded-checkpoint:已保留,完整同步和安装态验收没有由本源级切片结清。新版 RFC 的“每次发送前重新核验”是待实现的承诺,不是该承诺已经成立的证据。

关键代码讲解

  • loopx/extensions/lark/private_conversations.py 的 _deliver(819行)把规范化图片上传后得到的 image_keys 接入原 sender,保留原 message/Session 关联。source_authority_verifier(868–870行)会调用既有来源检查,但调用发生在 sender 的预检之前。
  • 同文件 before_send(929–933行)只冻结 text/started 并返回继续;sender 在外部预检完成后调用它,因此这是补最后一次授权检查的现有边界。
  • loopx/extensions/lark/return_files.py 的 _uploaded_resources(64行)复用 file 上传骨架,为图片在上传前固定 upload_started;未知确认不会再上传。verify_chat_images(126行)核对精确 post 和独立下载的完整字节,不能用上传 key 代替完成。
  • loopx/extensions/lark/presentation/markdown_post.py 的 _readback_post(195行)支持原始节点和 CLI 文本表示,检查有序图片、正文及既有 file 区域;少图、换序、额外节点和文本伪装均拒绝。
  • loopx/extensions/lark/post_delivery.py 的 deliver_post_parts(68行)把图片只放在最后一个实际 post,stall notice 不带图片;已知分段继续使用原回执和确定的 part 身份。

对主干的风险

[P1] 发送预检结束后必须重新核对原连接授权

位置:loopx/extensions/lark/private_conversations.py:868 的来源回调及929–933行的发送前回调。本人从真实 HTTP 提交一张图片进入原 Session,在实际 +messages-send --dry-run 返回时通过 Core disconnect 撤销该绑定。初始来源检查已经通过,随后当前回调仍返回继续,实际 +messages-send 发送了一条含图片节点的 post;之后的字节/来源读回才失败,outbox 留在 pending。全程仍是一条 Session、没有新增模型工作,问题不是伪造的授权回调。

不可变基础版本也复现了同一 sender 的文本预检窗口,但只发送一条旧占位文本、零图片;当前提交在这个未修复的边界首次增加了图片发送。因此我没有把共享旧缺陷误称为新函数引入,也不能把它归为与新增媒体无关的基础噪声。相反方向的反例也已执行:在图片上传预检期间撤权,现有上传后检查正确阻止了上传和发送,均为零,说明最小修复不需要再建授权系统。

在发送预检返回后、记录 started 和实际发送前,通过既有来源与 Core 授权 owner 再核验;每个分段都执行相同检查,拒绝时不发送。加入真实 HTTP/Core 回归:短消息及每个长正文分段在发送 dry-run 返回时撤权,实际发送数必须为零;同时保留上传前拒绝、未知效果不重发和原回执恢复。

原有七文件 focused suite 326 passed(330.12秒),标准 premerge 的 6 direct + 11 selected 检查全部通过、零人工 hold;这些成功不覆盖上述最后发送时序。正常独立 base/head 旅程为:基础版本零上传且 partial,当前两次上传、两次下载、有序图片且 delivered,重启不再写。最初错误测试路径未执行用例,修正后完整通过;早期独立夹具在发现 outbox 前读状态的问题也已纠正,原诊断保留。没有查询或等待 CI。

语义与 CI 对齐

本次复用原会话授权与回执词汇,没有新的通用 Goal 状态。fresh-source-before-write 是既有权威约束,不能靠新增字段、文档改写或绿色检查来替代。当前 required 的媒体撤权反例失败;应修复同一 owner 的效果前检查,而非放宽验证、清空历史回执或另开消费者。

我的整体评价

结论为 REQUEST_CHANGES,绑定 3672381d1632a97c2f73315c6f3b790913759318。正常图片交付确有用处,资源 helper 与原 outbox 的复用及兼容边界合理,代码量与问题相称;阻塞点是新增媒体仍可跨过明确撤权,长程安全继续和用户体验尚不能合格。资源 IO 复用既有 return_files,消息仍归原 Session/outbox,分段和回执仍归原 sender,放置合理;需要修复同一发送边界的最后授权检查,而非增设另一消费或状态 owner。当前 PR 的有用边界是原私聊中的有界图片交付及原回执恢复;该边界必须先补齐撤权后的发送阻断,安装态视觉与完整同步目标仍分开验收。

本轮实际验证 HTTP/Core、post 节点及资源读回;未在真实飞书客户端检查正文与图片的整页呈现,协议结构通过不等于视觉验收。真实飞书客户端的图片/长正文布局、窄屏和安装态双端采用未验证;现有预检撤权反例独立足以阻塞本提交。复评需要精确新提交补齐发送前授权并使上述反例通过;保留326项正反路径及原有旧回执/未知状态,不能从修复一个分支直接继承整个 PR 的批准。

English verdict: REQUEST_CHANGES — 3672381d1632a97c2f73315c6f3b790913759318 sends an image after its original binding is revoked during send preflight. The base has the same text-only sender timing gap but emits no images; this extension exposes media through it. Recheck current source/Core authority after preview and before every send/part.326 focused tests and standard premerge passed; the independent late-revocation test still fails. Live provider rendering and installed adoption remain unverified.

Comment thread loopx/extensions/lark/private_conversations.py
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
…sation-image-sync-20261026

Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>

@loopx-agent loopx-agent left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewer: model_agent; gpt-6.1-sol; OpenAI; runtime_reported; effort=max

Approval conclusion (author-owned PR; GitHub blocks formal self-approval)

动机

在 App 继续原飞书私聊并发送配图的用户。以前图片变成未同步占位说明,用户必须回 App 找图;现在原私聊收到完整正文和有序图片,发送前撤权则停止剩余投递。

真实 HTTP/Core/SQLite 旅程核验两张原图的发送、独立字节读回及重启后不再上传或发送;预检撤权的短消息和长分段均停止后续写入。本 PR 不增加模型任务、消费者或调度器,不负责完整历史、回复关系镜像及安装态双端验收。真实飞书客户端的图片布局与实际服务采用仍须独立验收;这次源码修复没有结清完整会话同步目标。两张图片出现在原对话中,与记录一个上传 key 或把 Todo 标成完成不是同一件事;因此这次同时保留图片内容、顺序、实际消息回执及资源读回。原有已发送占位说明也不能被升级过程改写成图片已经送达。

改动思路

资源 IO 复用既有 return_files,消息仍归原 Session/outbox,分段与回执仍归原 sender;最后授权检查放在既有发送前回调,不增设第二个状态或权限 owner。当前 PR 交付原私聊中的有界图片传输、独立资源读回和撤权安全恢复;完整历史及真实客户端采用沿原同步工作继续。

保留缺失图片的旧行为,会继续迫使用户在两个界面找资料;仅修改占位文本解决不了资源传输。另建桥接 worker、图片重试队列或同步权限则会增加重复消费者与状态分叉。这里选择在原会话的已提交消息上扩展已有资源 IO 和富文本发送器,正文、图片、长文分段与恢复均沿原身份完成。Python 的修改属于 provider IO 和已有展示适配,Core 的当前会话、绑定与受众仍由原 owner 决定。

资源 key 是 provider 实际确认的事实,上传开始意图用于保存未知效果;图片完成状态由原消息及独立字节验证派生。前两者不能仅从本地图片推导,也不需要人工再维护一套任务状态。发送前回调在外部预检结束后重新检查来源和当前授权,再冻结写入意图。首次发送、编辑及恢复后的每个实际分段使用同一回调;撤权后原样保留已确认前缀与未知回执。

具体改动

评审覆盖基线 56cedb4c886d1fceaddea4113f4a1c56fa934aee 到 deed9993dc5e0d27a187601c71a8f25611dd0790 的完整十文件差异(+542/-81),其中六个现有运行时/展示模块、两个回归模块和中英文 RFC。相对上次 3672381d1632a97c2f73315c6f3b790913759318,新增内容集中在最后一次授权检查、恢复分段传递该检查、五个真实 HTTP/Core 反例及双语边界说明;其余主干采用正常 merge,没有强推。

规范依据 docs/architecture/rfcs/app-conversation-and-async-inbox-v0.md,固定修订 56cedb4c886d1fceaddea4113f4a1c56fa934aee。本评审以描述性标签对齐原规范,而不把本 PR 新改的文字当作独立验收依据:

  • original-session-outbox:已实现。真实 HTTP/Core/SQLite 中只保留一个原 Session 和一个 App 输入,投影不会重新受理消息或增加模型工作。
  • fresh-source-before-write:已实现。短消息、长文两个实际分段、以及恢复后的剩余分段都覆盖预检返回时真实 Core disconnect;该边界不产生新的发送。
  • recover-known-attempt:已实现。已知消息定位符与资源 key 沿原回执验证;未知上传确认、无定位符发送和字节不符不改成已送达、不盲重试。
  • bounded-checkpoint:保留后续验收。完整历史、回复关系与真实安装态双端采用仍由原同步工作承担,源码级传输切片不替代它们。

关键代码讲解

  • LarkPrivateConversations._deliver 接收规范化图片,绑定原 App outbox。其共享发送前回调重新读取当前来源及 Core 授权,拒绝时不记录 started、不发送;已知分段恢复也携带这个回调。
  • _uploaded_resources 复用文件上传骨架。图片在效果前持久化 upload_started,随后保存真实 key;同 profile、绑定、摘要、名称和大小的缓存冲突或确认未知均保持失败,不另造资源。
  • verify_chat_images 检查最后一个实际 post,并独立下载每张图验证长度和摘要。错字节、来源变化或不可读不会使 outbox 成为 delivered。
  • _readback_post 同时处理原始 post 和 CLI 的规范表示;正文与图片顺序必须一致,少图、换序、额外节点和 text 类型伪装均拒绝,文件区域的既有语义保留。
  • deliver_post_parts 继续使用原分段 journal。只有最后实际分段带图片,stall notice 不带图;已确认前缀先读回,恢复不会把整个长文重新发送。

对主干的风险

最强反例是“预检前合法、预检返回时已经撤权”。上次精确提交在短消息、第一实际分段及第二分段三种情况下仍写入,另一个长文总计划预检已经正确拒绝;原有 347 项测试没有覆盖最后时序。当前源码将检查移至每个效果前的现有回调,并补上恢复分段的回调传递。新的独立回归通过实际 Core disconnect 修改授权,不靠手写一个永远返回 false 的授权假对象。

在当前主干的归档源码中先固定实际生产模块,再执行相同图片 oracle,得到预期的零上传失败;当前 head 则验证两次上传、两次独立下载及重启后零新效果。首次归档对照因 pytest 根路径抢先导入 head 而得到无效通过,已经明确剔除其资格;修正导入后打印了归档生产模块路径并复现旧缺陷,未把那次通过当作基线证据。

精确 head 的六文件兼容集合 352 passed(341.10秒),覆盖图片、私聊、图片接入、Markdown 与既有文件返回。标准 premerge 的 6 direct + 11 selected 全部通过,零人工 hold;Ruff、编译、双语 RFC 和本次公开候选扫描通过。没有查询或等待 CI。单独全局 check 仍报告既有 registry/authority 问题;其十文件 public boundary 子检查通过,标准 premerge 的独立边界检查也通过,未将全局状态谎报为绿。

语义与 CI 对齐

复用既有规范图片、绑定、partial 和实际回执语义,没有新增通用 Goal 状态、执行器或授权。无图片的可选参数不进入旧意图 digest,历史文本/文件回执与已发送占位说明仍可读;这不是一个新增默认关闭开关。已有绑定的图片交付是明确的默认行为扩展,授权检查也会让原文本/编辑路径拒绝预检期间发生的撤权。额外一次 fresh source/Core 读取有真实 IO 成本,本次没有声称延迟或费用收益。未知发送与上传继续持有,不以清空 journal、重放旧消息或放宽权限恢复。

我的整体评价

结论 APPROVE,绑定 deed9993dc5e0d27a187601c71a8f25611dd0790。长程继续能力在原回执、重启及未知效果分支上有实质改善;用户沿原 composer 完成一轮图片请求,不增加必需的配置、复制或再次确认。保留已部署的旧回执读法有实际理由,两个意图回调已合并为一个共享检查,资源 IO 也已复用原 helper;继续拆出一套图片服务反而会增加维护面。资源 IO 复用既有 return_files,消息仍归原 Session/outbox,分段与回执仍归原 sender;最后授权检查放在既有发送前回调,不增设第二个状态或权限 owner。当前 PR 交付原私聊中的有界图片传输、独立资源读回和撤权安全恢复;完整历史及真实客户端采用沿原同步工作继续。

检查了 App 发送入口和 rich-post 的实际调用链;本 PR 改 provider 传输与资源节点,未修改 LoopX 页面、CSS、组件或客户端布局。正文与图片顺序经真实后端的 provider 边界核对,未把协议检查称为视觉验收。真实飞书客户端及安装态双端的整页视觉与窄屏采用未验证,属于原完整同步验收,不能由本源码切片代替。本判断批准有用、可回滚的源码传输增量,不宣称完整同步、真实 provider 视觉或本机安装已经完成。剩余采用应保留原会话、未知效果与历史 partial,独立核验,不能通过再次发送旧占位消息来制造成功证据。

English verdict: APPROVE — deed9993dc5e0d27a187601c71a8f25611dd0790 delivers canonical App images through the original private outbox and existing resource/post owners. A final current-source/Core check now follows preview for sends, edits and resumed parts; real-Core late-disconnect regressions stop subsequent writes. 352 focused tests and standard premerge pass. Unknown effects and old partial receipts stay preserved. Live provider rendering, installed adoption and broader conversation synchronization remain separate acceptance.

@huangruiteng
huangruiteng merged commit d928e71 into main Oct 26, 2026
23 of 29 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants