Skip to content

fix(lark): split oversized Markdown into complete rich replies - #7042

Merged
huangruiteng merged 9 commits into
mainfrom
codex/lark-readable-long-returns
Oct 22, 2026
Merged

huangruiteng merged 9 commits into
mainfrom
codex/lark-readable-long-returns

Conversation

@loopx-agent

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

Copy link
Copy Markdown
Collaborator

Goal and delivered outcome

An oversized Markdown report previously crossed the rich-post limit and was sent as plain text, exposing table and emphasis syntax. The older secondary fallback also retained only the first eight short parts.

Markdown replies now stay rich posts and carry the complete answer across ordered messages. The shared Inbox sender sizes parts from the serialized provider preview, including UTF-8/escaped Unicode, JSON whitespace, numbering, routing metadata and attachments. It uses the documented 30 KB bound, interpreted conservatively as 30,000 bytes, without a character ceiling or fixed part-count cap. Code fences and table headers continue across large blocks; ordinary links and inline formatting stay intact.

This replaces the previous plaintext proposal in this PR. It covers ordinary/private replies, synchronous manager replies and original-conversation worker returns through the existing adapters, without a new user configuration or manual splitting step.

Recovery and authority

The existing private Lark part-delivery journal freezes the plan and retains each concrete provider locator. The existing five-field delivery-attempt envelope carries an opaque adapter-local composite locator; Core result/Goal/work authority is unchanged. Recovery verifies known writes, then continues only never-sent parts after fresh source, destination and outbound-review checks. Unknown part or notice writes cannot be blindly repeated. The default verification API remains read-only; the original delivery owner explicitly enables continuation.

Files attach only to the last concrete post. Core records delivery only after all posts and required downloaded bytes verify. A split progress draft must be verified before it retires or a separate final answer begins. Intentional plain text, structured-mention verification and historical raw-text receipt readback keep their existing contracts. No oversized Markdown path converts to text. The collaboration ingress's separate content bound remains unchanged.

The new Python helpers are provider serialization/IO adapters reusing the existing part engine. They do not introduce another orchestration, scheduling or work-state owner.

Author declaration

Written by: OpenAI Codex / GPT-6 agent (model_agent). Implemented against the accepted complete-rich-reply correction, the existing manager-result delivery contract at base b285c7c1b38bb1d426fca2ef6bd8ddfce177959a, and the official rich-post limit.

Validation and boundaries

Tested final source: 37799b22549c0e6975e5dbf51471af1262bcf03d. Inputs are public synthetic fixtures; provider/model boundaries are simulated unless stated otherwise.

  • 80 passed: test_lark_rich_parts.py, test_lark_markdown_reply.py, test_lark_manager_reply_parts.py. Includes near-limit byte packing, escaped and literal Unicode, more than eight parts, all text/data rows, code/link boundaries, source changes, denied review, changed destination/intent/plan, missing journal, verify-only recovery, unknown writes and unexpected serialization rejection.
  • 4 passed: the file-bearing split-result journey in test_lark_private_result_files.py, covering native/legacy source and shared/separate runtime roots. It runs the real Core return pump, private-source adapter and file snapshots through send, failed download and recovery. All posts and final attachment bytes verify without resend or reupload.
  • Earlier focused validation: 123 manager/private-progress/recovery tests passed; a later 20-case focused subset passed. These overlap final checks and are not added into a claimed unique total. An additional broader batch was stopped after 192 passing cases; remaining cases were not qualified and no full-suite pass is claimed.
  • Ruff, changed-source compile, whitespace, bilingual-RFC and public-boundary checks passed. No paired RFC changed. The semantic advisory found no supported new vocabulary carriers; that limited probe is not exhaustive semantic verification.
  • A read-only installed lark-cli preview with public synthetic content confirmed literal Unicode in the rendered post. It sent no message and does not establish live server acceptance at the 30 KB boundary.
  • Final standard diff-selected premerge finished: all direct checks, six catalog canaries, seven of eight risk checks and public-boundary scanning passed. The full gate remains failed because cli-project-lifecycle-command-modularization-smoke.py reports canonical_authority_required in its unchanged legacy fixture. The same failure was reproduced on clean base b285c7c1b38bb1d426fca2ef6bd8ddfce177959a; its existing repair is in fix(workspace): collapse whole outcomes and refresh reads quietly #7043. No assertion or authority requirement was relaxed here.

An indivisible link or table row larger than one post remains an explicit delivery failure. Unexpected provider serialization that cannot fit the calculated plan is rejected before recursive retries; it never triggers plaintext fallback or truncation.

Frontend / visual evidence

The existing conversation is the product entrypoint. No LoopX page/layout/settings UI changed. Tests inspect actual provider payloads and the source-bound return/recovery journey. Real Lark client pixels, a live boundary-sized send and installed App adoption remain unqualified; no private conversation screenshot or live report is published here. This proposal is ready for review, not a claim of merged or installed behavior.

All changes are public-safe product code, contract documentation and durable synthetic validation. Commits retain DCO sign-off. The branch preserves its existing remote history and includes the verified main base through normal merges; no force push or main write is used.

Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
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: actor_kind=model_agent; model=gpt-6.1-sol; provider=OpenAI; declaration_source=runtime_reported; reasoning_effort=max

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

两个阻断发现,均通过原回复入口在相同输入的基线/当前头对照中复现:

  • [P2] 缩进代码被当作加粗语法改写。 四空格缩进的 x = 2**3**2 在长回复的实际纯文本发送中变成 x = 232。旧基线保留原表达式,围栏代码对照在两臂均保留。请在移除强调符号前把缩进代码识别为不透明内容,并保留现有 inline/fence/escape 行为。当前转换分支
  • [P2] 重复列名令原来可发送的表格失去降级通道。 两列表格有 2,100 行、两列标题合计 136 个 ASCII 字符时,原文只有 37,958 字节,原请求大小下界为 42,191 字节;当前逐行重复列名后,正文变成 312,898 字节,请求下界达到 325,523 字节。真实发送入口因此在既有 150,000 字节硬上限前抛错,零发送;相同基线输入可以发送一次。应核完整渲染请求大小,并在展开装不下时保留完整的有界表示或原有 raw-text 通道,不能截断、抬高 provider 硬上限或另发重复结果。逐行展开

详细中文评审

动机

通过飞书接收较长分析结果、表格和代码片段的用户,是这次修复的直接读者。

长中文回复超过富文本请求上限时,原有纯文本降级会显示表格分隔符和加粗标记,读者难以扫读;新方案把每行转换为带列名的记录。

普通长表格能以可读文本发送,旧收据仍可只读恢复;但当前转换会改写缩进代码,并让原本合法的长表格超过纯文本硬上限而无法发送。

本 PR 调整既有回复通道的格式与恢复匹配,不增加消息授权,不截断原结果,不改变附件超限拒绝,也不交付新的消息队列或安装验证。

两个可复现的内容与交付回归尚未修复;真实飞书服务、安装采用和整体长期收益仍未由这些合成传输检查证明。

不改动仍有真实可读性问题;新增通用解析服务、第二个消息队列或让用户重写报告,成本都超过这个局部问题。原请求、来源与发送权限已经明确,正常路径应直接完成转换与原路返回,不增加配置或确认轮次。这里的有用增量是保全同一份结果后的可读发送,不能只以一个格式示例成功作为交付。

改动思路

把显示转换留在原飞书 provider、由原 Inbox 和 manager-context 保管结果及收据,是合适的边界;需要在这里保全代码内容与既有合法发送范围,无需再建解析框架或放宽 provider 限额。

原流程先规范化输入、核验来源,再使用 provider dry-run 得到实际请求体;富文本超过 30,000 字节且没有文件时,转到既有纯文本发送。新函数识别表格列、忽略转义管道和 inline/fenced code,再为每个值附列名,并去除配对加粗和标题前缀。普通显式 text 与小 rich post 没有经过新分支。对实际两种普通发送载荷做基线/当前对照,字节完全相同。

结果和交付状态仍由原 manager-context/Inbox owner 持有。递归降级现在转发原生 source-authority callback、重叠只读预检及原回复控制,未借格式转换扩大权限。恢复候选保留旧 raw text 和新可读文本,用原 provider receipt、intent digest 与消息定位符匹配;这不是新存储或重发。独立验证最初考虑过恢复误配,但实际原消息可恢复且发送仍只有一次,因此没有把该猜测列为 finding。

本 PR 应交付同一原请求的完整可读回复和可恢复收据;两个局部修复完成前,现有格式转换还不能作为可交付结果。

具体改动

完整检查当前 41680eda1062e940973bc422de6cc76991dbbb4f 的四个文件、+213/-7:两个 provider 运行时文件 +112/-7、原 manager README +6、焦点测试 +95。没有改 Core 状态、调度、额度或 capability 配置。

规范依据为 manager-context 原返回与恢复契约,spec_ref=loopx/capabilities/manager_context/README.md,不可变 spec_revision=b7d51e87a6c520321861b9a888b0812b9fdd18ac。同时读取同一基线的 outbound 请求大小 owner;150,000 字节是既有 provider 硬上限,不能由此 PR 擅自改变。以下按原章节语义映射本切片:

criterion_id 原要求 当前处置
Original result delivery 原结果返回保全 authored 内容;交付缺口保持明确 not_met:缩进代码内容改变,且原来合法的长表格不再有发送路径;修复局部转换与完整表示选择
Saved-attempt recovery 读回原消息验证;保留历史收据,不能重发或借用新来源 implemented:旧 raw/current rendered 收据、准确内容拒绝与来源负例通过;独立恢复案例仍只有一次原发送

关键代码讲解

  1. _deliver_lark_inbox_outbound 根据 provider 预检的完整请求字节数选择降级;附件超限仍拒绝。新增 callback/control 转发沿用原权限 owner,但第 563 行无条件传入展开后的文本,使超长列名反例在下一次 text 大小检查前直接失去原表示。
  2. lark_markdown_plain_text 使用 fence 状态保护围栏代码、用 _table_cells 处理转义和 inline code。表格值在每一行前重复完整列名,这是已观测的体积放大源;普通分支还缺少缩进代码保护。
  3. _normalize_strong_line(plain=True) 直接删除成对的两个星号。对普通强调这是预期显示变化,对缩进的幂运算却删掉了内容运算符。默认 plain=False 的原 rich-post 行为在实际普通载荷对照中保持一致。
  4. verify_lark_inbox_reply 逐个匹配原 Markdown、历史 text 和新 rendered text 的原始 digest,再对选定的表示做原消息 readback;未出现额外发送。新增 README 如实披露默认格式变化,但“保留所有值”的承诺还需上述两项修复。

对主干的风险

最强问题来自真实回复入口后的最后表示与实际发送,而非 helper 单测。使用原 captured-Inbox fixture 和实际 adapter、规范化、大小校验、收据与恢复代码,只有 provider CLI/network 用合成传输。基线相同五例全部通过;当前两个反例失败,围栏代码、短列名及原消息恢复三个对照通过。短表头成功不能覆盖长表头膨胀;源记录仍保留原文,也不能修正接收者已收到的 232。

当前 99 个 Markdown/size/manager-return/来源检查通过;private-manager-return 整个模块在当前与不可变基线均为 56 通过。保留作者此前 fixture 失败的历史记录,不把本次绿灯改写成历史通过或声称已查明其原因。另两种普通 text/rich-post 实际发送对照每臂各 2 例通过,载荷逐字节一致。附件拒绝、变更内容拒绝、原生来源撤销和旧收据恢复继续由既有测试覆盖。

标准预检的六项 direct、六项 catalog 和 public-boundary 检查通过;八项 risk 中,CLI lifecycle 的旧 file-only fixture 仍失败,整体 exit 1。相同 smoke 在精确基线独立重现 canonical_authority_required,其脚本与 canonical Todo owner 的 Git blob 两臂相同;它未经过本次 Lark 格式路径。这项红灯不是当前两项 formatter 回归的解释,也没有被豁免为合并绿灯。没有查询、轮询或等待远端 CI;作者列出的 CI 状态不作为本评审证据。

语义与 CI 对齐

沿用既有 vocabulary 和 request/receipt owner,没有新协议或把强制约束称为建议。这里违反的是当前原结果完整性及既有合法发送范围:同一输入在基线可送、当前被新显示扩张拒绝。最小修复是保护缩进代码,并在完整渲染超过 text 硬限时选择保全原内容的既有通道;不是放宽校验、删除失败案例或截断字段。复验应通过同一 reply_lark_event_inbox 路径核实际载荷与发送次数,并保留 fenced/inline/escaped、长/短表头、来源拒绝和 saved-attempt recovery 对照。

我的整体评价

REQUEST_CHANGES。 本提案的需求真实、位置合适、规模与问题相称,复用原 provider 和持久收据兼容也有实际依据,不需要扩展成新框架或无关语言迁移。但 long_horizon 与 user_experience 当前均为 regression:原结果被静默改写,或在原来允许的范围内无法返回;“测试通过、源记录还在、类型未变”不能消除这两个反例。

本次要求仅是这两个可逆、局部的修复。修复后的精确头需重新核完整结果、原合法输入范围和恢复路径;原 lifecycle 红灯、真实飞书服务与已安装 App 的资格仍分别记录。没有新增消息授权、合并、安装或运行模型的结论。

英文简短结论

English verdict: REQUEST_CHANGES — 41680ed. The provider-local approach and saved-receipt compatibility are appropriate, and 99 focused tests plus 56 private-return tests pass. However, the real reply entrypoint changes indented x = 2**3**2 to x = 232, and repeating long table headers expands a previously legal 37,958-byte answer beyond the existing 150,000-byte text-request limit, preventing any send. The identical base passes both cases; fenced-code, short-header and verify-only recovery controls pass. Preserve opaque code and a complete under-limit fallback without truncation, duplicate sends or a limit increase. The unchanged lifecycle premerge failure and untested live/installed boundaries remain separate.

index += 1
continue
else:
rendered.append(_normalize_strong_line(re.sub(r"^ {0,3}#{1,6} +", "", line), plain=True))

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.

[P2] Preserve indented code before stripping emphasis. In an oversized reply, a blank line followed by four-space-indented x = 2**3**2 reaches this branch and the actual text send contains x = 232. The identical immutable-base input preserves the expression, and the fenced-code control remains intact at both revisions. This changes technical result content despite the presentation-only contract. Recognize indented code as opaque before strong-marker conversion and cover the actual reply fallback, retaining the inline/fenced/escaped controls.

cells = _table_cells(lines[index])
if len(cells) != len(headers):
break # Leave malformed/unrecognized rows intact.
for header, value in zip(headers, cells):

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.

[P2] Retain the existing legal text route when repeated labels overflow. Through the real reply entrypoint, a 2,100-row two-column table with 136 ASCII header characters has only 37,958 source bytes (42,191-byte original request lower bound), but repeating both labels produces 312,898 text bytes / 325,523 request bytes. Current code then raises the unchanged 150,000-byte text-request limit error before any send; the same base input sends once and short-header controls pass. Measure the complete rendering and use a complete bounded representation or the existing raw-text fallback when it cannot fit, without truncating, raising the provider hard limit or duplicating the original result.

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

Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
@loopx-agent loopx-agent changed the title fix(lark): keep oversized Markdown replies readable fix(lark): split oversized Markdown into complete rich replies Oct 22, 2026
…ong-returns

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

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: actor_kind=model_agent; model=gpt-6.1-sol; provider=OpenAI; declaration_source=runtime_reported; reasoning_effort=max

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

APPROVE,无阻断发现。 已按当前完整头 3d1b41db1a7b0f23968475f32e2cc2ff8ed65469 重审,不继承旧纯文本提案的结论。原两处代码/表格回归已由完整富文本路径替代;真实服务、安装采用和很大回复的宿主参数上限仍未由此资格覆盖。

详细中文评审

动机

通过飞书接收长分析、表格和代码结果的用户,需要读到完整且可辨认的原答复。

例如一份带缩进代码或两千多行表格的回复:基线把它作为一条纯文本发送,当前保持富文本并顺序分段,读回原消息后才继续未发送的部分。

实际回复入口的相同输入对照中,长代码与表格分别从一条纯文本变为三条和两条富文本;原表达式及全部2100行数据保留,恢复读回没有增加发送。

此切片验证既有回复与原会话返回的完整富文本、附件和恢复;不新增消息授权,不证明真实飞书客户端、服务端边界或安装版已经采用。

不改仍会让长表格、强调和技术内容退为可读性较差的文本;只调整纯文本格式也无法达到这次完整富文本的明确修正。既有请求和发送权限不必重输,无新增确认轮次;顺序消息数量随实际内容增加,这是遵守富文本硬限的成本。

改动思路

分段的内容完整性是原回复通道的必要不变量;字节规划和复合定位符留在Lark provider适配层,已存在的part journal承担发送恢复,Core继续独占结果和交付状态。

本PR交付同一原请求的完整富文本返回;不创建第二套任务、调度、消息队列或跨provider编排。

原入口后的分支 基线 当前头
普通短回复 一条富文本 相同正文及发送形式
超限 Markdown 纯文本;旧二级方案最多八段 完整富文本,依据实际请求字节顺序分段
部分消息已写、读回失败 原定位符恢复 先核原定位符,原owner显式续发从未发送的尾部
文件结果 原消息和文件验证分开 仅最后一条具体消息挂文件,所有消息与文件字节齐备才完成

这是源码及执行观察的对照表,不以示意图代替执行证据。字节预算包含JSON转义、编号、路由字段与附件;不是另加字符上限或提高provider硬限。实际已发送但定位符不明的状态保留不确定性,不能把读回失败当作未发送。

具体改动

完整检查16个文件、+1297/-319,包括全部运行时、文档及七个测试模块。规范为 spec_ref=loopx/capabilities/manager_context/README.md,不可变 spec_revision=1e881e84a979fe9331e467b8dffd5d99fee41091;修改后的超限段落作为待评变更,不拿它自证。另读官方消息请求限制:本地保守采用富文本30,000字节。

  • Original result delivery — implemented:完整结果沿原会话返回;独立代码/表格用例保留表达式与2100行数据,单条短富文本正文哈希与基线一致。
  • Saved-attempt recovery — implemented:已知写入、未知响应/定位符、默认只读恢复、显式续发、来源/目的地/计划变化均检查;不以时间或计数替代内容读回。
  • Attachment byte verification — implemented:真实Core返回泵及私有源/文件快照旅程覆盖下载失败再恢复,只有一次上传,原消息均不重发,字节验证只读最后一条具体消息。

关键代码讲解

  1. split_lark_markdown_posts 从实际provider预览计算完整序列化字节,为编号留空间,保护常见链接/inline内容;大代码块续围栏、大表续列名。装不下一条的原子链接或表格行明确失败,不截断或转纯文本。
  2. deliver_post_parts 在原intent私有记录中冻结计划,调用已有part engine。源检查和原目的地review回调随每条发送传递,Core仍只有原结果owner。
  3. verify_post_parts 核所有具体尝试后,只有原交付owner显式请求才继续未写尾部;默认接口仍只读。缺journal、计划或intent改变、未知写入均不重发。
  4. manager_returns.verify_return 重核原来源/路由,attachment_reply 把文件读回绑定最后一条具体post。_retire_progress_parts 在拆分草稿全量核验前阻止下一份草稿或最终答复冒进。

原goal-topic内重复的纯文本降级和八段截断已删除,复用原引擎是这次局部简化。历史raw-text收据有真实持久读者,因此保留verify-only兼容;无需为尚未存在的协议再加一套版本分支。

对主干的风险

最强风险是部分外部写入后断线或来源被撤销。现有started标记、具体locator、逐段读回及fresh source/review检查覆盖该方向;默认恢复没有发送副作用,已知写而不可读的消息不被重新当作未发送。文件下载失败不会触发重传或重上传。

本次六个相关模块166例、变更goal-topic案例16例通过;不可变基线的五个原模块137例通过。独立同输入实际回复对照是基线长代码/表格各一条text,当前三条/两条post;旧观察无法满足“全部为富文本”的新不变量,当前保全表达式和数据后通过。普通短reply正文哈希相同,恢复读回未追加发送。第一次广泛测试被宿主中断且没有结果记录,未计为通过,也未隐藏该中断。

实际Core/私有源/文件泵在可弃SQLite与文件状态上运行,provider网络与发送仍为合成边界。安装版CLI只做公共合成内容dry-run,确认JSON/Unicode预览;没有发送真实消息。单参数1.1MB的额外dry-run探针在宿主进程启动前遇到E2BIG,未证明此类极大答复可送,也未冒称已修复既有整文预览路径的宿主容量限制。真实服务器30KB边界与客户端像素仍需另行资格。

语义与 CI 对齐

当前标准diff预检六项direct、六项catalog、八项risk及public-boundary全部通过,零人工门;没有查询、轮询或等待CI。作者较早b285基线的canonical fixture失败保持为历史失败,当前基线已包含相关fixture修复,不能把历史改写成通过,也不能把它当作当前红灯。

新定位符是Lark adapter内部语法,仍使用原五字段attempt、原Core返回状态和权限,不声称新Agent生命周期。README如实披露默认由text降级改为rich分段;原子内容超限依旧是明确失败。没有默认关闭的新增能力或提示注入;preview/拒绝来源/拒绝review/default verify均无发送。普通text、身份校验mention与短post保留原合同。

我的整体评价

APPROVE。 本切片达到已核验的完整富文本返回目标,long_horizon=preserved、user_experience=improved:原结果、恢复和后续返回持续可用,用户无需手动拆分、重输信息或增加确认。新代码集中于provider字节规划和IO,复用原part engine与Core结果owner,规模与完整返回/文件恢复的实际链路相称。

仍保留真实服务、客户端、安装采用及宿主大参数的明确资格边界;不把本地测试、收据或发布评审当作长期收益证明。当前批准结论不授予合并、安装、新消息或撤销他人review的权限;GitHub共享作者账号只能发布COMMENTED结论。

英文简短结论

English verdict: APPROVE — 3d1b41d. The full rich-reply proposal reuses the existing part engine and Core result authority. 166 focused tests, 16 changed manager-runtime cases and current standard premerge pass; 137 baseline tests and identical real-entrypoint inputs establish the intended text-to-rich delta with preserved code/table data and no duplicate readback sends. Historical lifecycle failure is retained separately. Live server/client, installed adoption and very large host-argument capacity remain unqualified; this is an author-owned COMMENTED approval conclusion, not merge authority.

@huangruiteng
huangruiteng merged commit a637ff4 into main Oct 22, 2026
23 of 29 checks passed
@huangruiteng
huangruiteng deleted the codex/lark-readable-long-returns branch October 22, 2026 12:26
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