Skip to content

fix(lark): fail closed on damaged inbox state - #7979

Merged
huangruiteng merged 1 commit into
loopx-project:mainfrom
Duang777:codex/lark-inbox-state-fail-closed-20261103
Nov 3, 2026
Merged

huangruiteng merged 1 commit into
loopx-project:mainfrom
Duang777:codex/lark-inbox-state-fail-closed-20261103

Conversation

@Duang777

@Duang777 Duang777 commented Nov 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Treat only a missing operator-inbox processed state or material-review receipt ledger as empty.
  • Propagate existing wrong-type or unreadable state so acknowledged messages cannot silently re-enter the pending queue.
  • Add red-green regressions for processed-state and receipt-ledger path damage.

Root cause

Path.is_file() returned false for both a missing path and an existing directory. The readers collapsed those two states into the same empty-state result, which reopened acknowledged messages and could report reply_due again.

Test plan

  • Red run: both new path-damage regressions failed before the source fix.
  • Green run: 89 focused inbox, urgency, and reaction tests passed.
  • Ruff passed for all changed Python files.
  • Semantic vocabulary drift smoke passed.
  • Standard diff-selected premerge passed at 1afeb7b652583f9b36d9c999ee5b23fdbcefd2cc.

Validation notes

Changed surfaces: control plane, Lark extension runtime, and Python tests. The premerge report retained one inherited maintainability advisory for render_canonical_todo_sections; it does not mention or overlap the changed files. All eight selected risk-profile smokes passed. Merge authority remains with the maintainers.

Path.is_file() treated existing non-file durable state as missing, reopening acknowledged events and emptying receipt ledgers. Read directly and only accept FileNotFoundError as absence so damaged authority cannot replay work.

Signed-off-by: Duang777 <duangjl007@gmail.com>

@loopx-agent loopx-agent left a comment

Copy link
Copy Markdown
Collaborator

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 · reasoning_effort=max

精确评审 head:1afeb7b652583f9b36d9c999ee5b23fdbcefd2cc;不可变差异基线:2afe4b0a0d2a45b5f7a172f01bdc0542adcdec52。未发现当前改动的阻塞问题;独立结论为 APPROVE。

动机 / Motivation

读取飞书收件箱并处理、确认消息的操作者与执行 Agent。

消息已经确认后,若 processed.json 被目录替换,旧版会再次显示它待处理;修复后返回明确读取错误,恢复原状态文件后继续保持已确认。

本轮真实 CLI 对照验证了损坏时拒绝、修复后恢复,以及材料结算的幂等重放;正常缺失文件和禁用收件箱仍可正常读取。

本评审不发送飞书消息、不修改真实收件箱,不认证长期采用、存储迁移或合并资格。

改动思路 / Approach

仅缺失文件可以代表空状态,这是既有读取边界的不变量;保留通用 urgency 与 Lark provider 的既有所有权,无需新增日志或通用恢复框架。

当前切片覆盖三个已存在的文件读取点及其真实读取、确认、材料结算入口,不改变消息授权、锁和写入格式。

不修改会让一次目录损坏重新唤起已确认消息。仅修 Lark drain 会让 generic urgency 仍错误安排回复;给三处读取再包一个通用恢复器则增加无必要结构。直接读取并只捕获 FileNotFoundError,是这条既有 IO 边界内更小的修复。原 schema 校验、per-inbox lock、ledger-first 写入及 ACK 权限不变;错误时先恢复真实状态,再沿原操作继续。

具体改动 / Changes

接受依据:loopx/extensions/lark/docs/lark-event-inbox.md,spec_revision 2afe4b0a0d2a45b5f7a172f01bdc0542adcdec52。下列 criterion_id 是本次映射标签,不是新协议。同时检查 原 Lark authority contract:消息可见性不授予 Turn/回复权限,重复与重启恢复须幂等。

  • INBOX-ACK:implemented。A message is acknowledged only after its effect is written back; repeated acknowledgement is idempotent.
  • LEDGER-REPLAY:implemented。Receipt replay/conflict, ledger commit and processed ACK share one lock; interruption recovery repairs the missing ACK.
  • DISABLED-READ:implemented。Disabled/no-inbox paths stay quiet and do not confer Lark reply authority.

关键代码讲解

  • _pending_events:Wrong-type path no longer treated as empty. 输入和关键分支:Missing/valid/directory/parent-file/malformed state. Only FileNotFoundError yields absence. 现有调用:event_inbox.py:502 and operator_inbox.py:272。
  • _load_processed:Direct read propagates path errors. 输入和关键分支:Same state matrix with disabled inbox and read-only drain. No acknowledged message becomes pending due to wrong path type. 现有调用:event_inbox.py:477,681,759; inbox_reactions.py:181。
  • _material_review_ledger:Only missing ledger initializes empty receipts. 输入和关键分支:Wrong ledger type and recovery, then replay. Do not invent an empty ledger over damaged state. 现有调用:event_inbox.py:872 before processed ACK。

完整差异3文件 +63/−5,生产2文件 +10/−5,原测试模块 +53。独立 inbox/material/urgency/reaction 模块89 PASS,包含两个新路径损坏回归、并发结算、同消息重放冲突、ledger已写但ACK未写及共享锁路径;Ruff、配置的17文件 mypy、diff advisory、完整 semantic 和3路径 public scan 通过。

另外用同一冻结 fixture 在真实 source CLI 和真实文件状态运行24个语义观察:基线18 PASS/6 FAIL,当前24 PASS。覆盖 missing/valid/directory/parent-not-directory/bad-JSON、禁用路径、恢复原ACK以及材料结算重放。17个完整观察相同,另一个只差真实 oldest_pending_age_seconds 的1秒;原始值保留,未删除错误、身份、状态或指令。被损坏的读取和 preview 没有改写已有 JSON;恢复状态后仍是两项材料待处理,重复材料结算返回 already_settled 且 write_performed=false。临时 extension 激活仅限合成 runtime,没有连接飞书。

对主干的风险 / Risks

最强反例是已确认的消息仍存在于 event files,而 processed/receipts 路径变成目录或父级文件;仅测试空目录或成功 ACK 会漏掉这个错误。六个基线反例均在当前拒绝。修复有意把这类旧“成功空状态”改成可见错误,而真正没有文件的初始化仍成功。错误本身不等于恢复;本轮实际恢复原状态并完成正常重放。路径权限/原生Windows尚未独立资格化,不从 POSIX 的目录错误推断所有 ACL。

标准 premerge:6项直接检查,2项 catalog 与8项 risk checks,没有阻断失败/人工 hold;其中一个 catalog 项保留 inherited maintainability advisory。原 ratchet FAIL 在基线/当前均输出相同393字节、空stderr,报错 render_canonical_todo_sections 的完整因果 source 未改;不是本 PR 引入的错误。严格质量回执 cqr_ef5ee6563e42e48d40bf 仍为 FAIL,没有借本次批准清除或豁免;合并/质量门保持独立。

一次初始pytest调用使用了不存在的文件名,没有运行测试;修正选择后才计89项。首轮 public scan 的默认路由/空临时registry调用失败保留,补全隔离registry后扫描通过。没有修改产品、降低断言或把调用失败重标为通过。

语义与 CI 对齐

复用 OperatorInboxSourceContract 和既有processed/ledger schema,不新增布尔状态、枚举、CLI或迁移。通用读取错误保持领域中性;Lark术语仅存在于provider。没有把强制拒绝称作建议,也没有扩大可见消息的执行/回复权。三个读取点以同一缺失规则修复,保留不同解析返回职责。

本轮未查询、轮询或等待远端 CI;仓库全量测试、原生Windows、安装态/实际模型采用与持续完整成本未验。评审不把未知CI视为缺证,也不把本地通过写成远端CI通过。

我的整体评价 / Verdict

APPROVE 当前精确头。long_horizon=improved、user_experience=improved 仅限这条已验证的失败/恢复边界;普通既有任务零额外确认轮次,错误可读且能沿原 owner 继续。仅缺失文件可以代表空状态,这是既有读取边界的不变量;保留通用 urgency 与 Lark provider 的既有所有权,无需新增日志或通用恢复框架。 当前切片覆盖三个已存在的文件读取点及其真实读取、确认、材料结算入口,不改变消息授权、锁和写入格式。

已检查 CLI/受管结果入口及完整差异,没有渲染页面、首屏、布局或控件改动,视觉评审不适用。 视觉未验证范围:none;本次没有渲染表面变化。

已应用的有界简化是删除重复/错误的分支并复用当前 owner,不需要新的通用框架或仪式性后续任务。回忆经验中“先验证可用结果与恢复”的建议在这里得到局部执行;它既不追加父路线门禁,也不证明 memory utility 或长程净收益。没有当前P0–P2 finding;保留上述质量FAIL和未验边界,本结论不授予合并、安装或外发权限。

English verdict: APPROVE the exact head. Damaged processed/receipt paths fail closed across the real inbox CLI and urgency reader; missing-state initialization, disabled paths, restoration and idempotent replay are preserved. Independent24-case comparison repairs six baseline failures. Strict quality FAIL remains a separate hold; live Lark and native Windows were not qualified.

@huangruiteng
huangruiteng merged commit a67d11c into loopx-project:main Nov 3, 2026
24 of 35 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.

3 participants