fix(lark): verify provider-normalized Card 2.0 callbacks - #4375
Conversation
|
@LIHUA919 可否在你当前 M3 known-locator 恢复切片之外,独立 review 一下本 PR 当前 head 请重点检查:
当前作者侧证据:436 个 这是 runtime/permission-sensitive PR,仓库政策要求独立 review;请把结论直接留在 PR,发现阻塞就 request changes。谢谢。 English: Please independently review the exact head above, focusing on immutable message/action binding, idempotent callback and restart behavior, delivery-only result recovery, value-free diagnostics, and unchanged effectful-operation authority. The 436 adjacent tests pass. A live simulation-only request was recovered and its original message patch was read back, but that is not being presented as a fresh automatic callback qualification. Please leave findings on the PR and do not merge it. |
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
35c6227 to
a85dd22
Compare
huangruiteng
left a comment
There was a problem hiding this comment.
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
结论 / Conclusion:APPROVE。我按 a85dd22cba3d4ceb24bab763147a5f759972ae1d 完成了 exact-head 自审;没有遗留阻塞项。结构化 review result 已通过 loopx pr-review --check-result 一致性校验。
动机 / Motivation
Card 2.0 的提交 JSON、消息读回和 card.action.trigger 并不是同一字节表示。旧实现把它们按完全相等校验,导致一张已送达、已被授权用户点击的卡片仍可在 claim 前被拒绝,且原卡没有结果反馈。历史诊断没有保存足够阶段信息,因此本审查不臆测旧 ValueError 的精确字段;可以确认的是:回调到达、操作未 claim、可见反馈缺失。
The change fixes a real provider-projection mismatch while preserving the existing operation authority boundary. Copy-only changes or dropping card verification would not solve both feedback reliability and action identity.
改动思路 / Design
复用现有三类 owner:Lark adapter 负责 provider projection;ChatActionStore 继续独占 operation lifecycle/claim;现有 Goal Channel delivery session 继续负责 Bot、chat、message 和 readback。新 delivery snapshot 只写一次,并须与既有 card_digest 相等。回调仍须同时满足 message/chat/app、action digest、授权 principal、tenant membership 和 lifecycle 检查,provider-normalized card 只是其中一个证据。
Result delivery recovery is deliberately M3/known-locator only: it patches and reads back the recorded message after an outcome already exists; it never reruns a model, worker, claim, executor, or venue write.
具体改动 / Concrete changes
message_card_matches统一 Card 2.0 exact/visible projection 比较;带 callback 的历史卡片禁止仅凭可见文本去重,避免同文案、不同 operation 误绑旧 action。record_operation_delivery_snapshot在既有 delivery 下保存 immutable submitted card,并对 digest 与重复写入 fail closed;旧记录使用 canonical proposal 重建后仍校验原 digest。- callback handler 接受受限的 user-card-content / legacy placeholder 投影,但保留全部独立身份和权限检查;同一 exact event replay 复用 durable outcome。
- result-card readback 使用同一 semantic verifier;失败后仅对 known message 做补送达与读回。
- collector 保存稳定 failure code、stage 和不含原值的 event shape,并通过普通 inspect 投影;repair pattern 记录了中英文可复用诊断。
Key reviewed symbols: message_card_matches, record_operation_delivery_snapshot, _callback_card_content_matches, recover_goal_channel_operation_results, and handle_goal_channel_operation_callback.
对主干的风险 / Main-branch risk
自审中发现并已修复的最强反例是:两张可见文本相同、callback identity 不同的卡片可能复用旧 message。当前 exact head 对 actionable history 禁止 normalized-only match;若 provider 无法给出完整 action identity,安全退化是额外发送一张受 idempotency 保护的卡,而不是误绑旧 action。
The remaining provider risk is an undocumented future Card 2.0 shape. It fails closed and now exposes a stable code/stage/value-free shape. A fresh live click after installing the merged build remains operational qualification evidence, not an excuse to weaken pre-claim checks.
我的整体评价 / Overall assessment
范围与问题严重度相称:没有新增 daemon、queue、database、broker authority 或第二套 UI/state owner;新增状态是 existing delivery/status 下的 additive evidence。442 个 Lark/operation tests 全绿;premerge gate 的 4 个 direct checks 与 12 个 canary/smoke checks 全绿,零 warning/manual hold,self_merge_allowed=true。DCO-only rebase 前后 tree 完全一致,六个 outgoing commits 的 author、committer 与 Signed-off-by 均为已验证 noreply identity。
No blocking finding remains. Verdict: APPROVE.
huangruiteng
left a comment
There was a problem hiding this comment.
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
结论 / Conclusion:APPROVE。我按 a85dd22cba3d4ceb24bab763147a5f759972ae1d 完成了 exact-head 自审;没有遗留阻塞项。结构化 review result 已通过 loopx pr-review --check-result 一致性校验。
动机
Card 2.0 的提交 JSON、消息读回和 card.action.trigger 并不是同一字节表示。旧实现把它们按完全相等校验,导致一张已送达、已被授权用户点击的卡片仍可在 claim 前被拒绝,且原卡没有结果反馈。历史诊断没有保存足够阶段信息,因此本审查不臆测旧 ValueError 的精确字段;可以确认的是:回调到达、操作未 claim、可见反馈缺失。
The change fixes a real provider-projection mismatch while preserving the existing operation authority boundary. Copy-only changes or dropping card verification would not solve both feedback reliability and action identity.
改动思路
复用现有三类 owner:Lark adapter 负责 provider projection;ChatActionStore 继续独占 operation lifecycle/claim;现有 Goal Channel delivery session 继续负责 Bot、chat、message 和 readback。新 delivery snapshot 只写一次,并须与既有 card_digest 相等。回调仍须同时满足 message/chat/app、action digest、授权 principal、tenant membership 和 lifecycle 检查,provider-normalized card 只是其中一个证据。
Result delivery recovery is deliberately M3/known-locator only: it patches and reads back the recorded message after an outcome already exists; it never reruns a model, worker, claim, executor, or venue write.
具体改动
message_card_matches统一 Card 2.0 exact/visible projection 比较;带 callback 的历史卡片禁止仅凭可见文本去重,避免同文案、不同 operation 误绑旧 action。record_operation_delivery_snapshot在既有 delivery 下保存 immutable submitted card,并对 digest 与重复写入 fail closed;旧记录使用 canonical proposal 重建后仍校验原 digest。- callback handler 接受受限的 user-card-content / legacy placeholder 投影,但保留全部独立身份和权限检查;同一 exact event replay 复用 durable outcome。
- result-card readback 使用同一 semantic verifier;失败后仅对 known message 做补送达与读回。
- collector 保存稳定 failure code、stage 和不含原值的 event shape,并通过普通 inspect 投影;repair pattern 记录了中英文可复用诊断。
Key reviewed symbols: message_card_matches, record_operation_delivery_snapshot, _callback_card_content_matches, recover_goal_channel_operation_results, and handle_goal_channel_operation_callback.
对主干的风险
自审中发现并已修复的最强反例是:两张可见文本相同、callback identity 不同的卡片可能复用旧 message。当前 exact head 对 actionable history 禁止 normalized-only match;若 provider 无法给出完整 action identity,安全退化是额外发送一张受 idempotency 保护的卡,而不是误绑旧 action。
The remaining provider risk is an undocumented future Card 2.0 shape. It fails closed and now exposes a stable code/stage/value-free shape. A fresh live click after installing the merged build remains operational qualification evidence, not an excuse to weaken pre-claim checks.
我的整体评价
范围与问题严重度相称:没有新增 daemon、queue、database、broker authority 或第二套 UI/state owner;新增状态是 existing delivery/status 下的 additive evidence。442 个 Lark/operation tests 全绿;premerge gate 的 4 个 direct checks 与 12 个 canary/smoke checks 全绿,零 warning/manual hold,self_merge_allowed=true。DCO-only rebase 前后 tree 完全一致,六个 outgoing commits 的 author、committer 与 Signed-off-by 均为已验证 noreply identity。
No blocking finding remains.
English verdict: APPROVE at exact head a85dd22.
Why / 动机
Lark Card 2.0 has multiple provider projections: the submitted JSON, normalized message readback, and callback
user_card_content. Treating them as byte-identical made a valid button click fail after delivery, leaving the original card without a result receipt.飞书 Card 2.0 在发送、消息读回和回调
user_card_content中具有不同的提供方投影。把这些投影按字节完全相等校验,会让有效点击在送达后被拒绝,原卡也无法更新结果回执。Closes #4370.
What / 变更
Strictly compare the complete visible Card 2.0 semantics across provider-normalized projections.
Keep the immutable submitted-card digest and the existing exact message, route, app, action-digest, membership, and authorized-principal checks.
Retain a bounded legacy title/placeholder fallback after those independent bindings pass.
Rebuild the immutable confirmation projection for idempotent callback replay without redispatching the operation.
Expose a stable public-safe callback failure code for diagnosis.
对平台归一化后的 Card 2.0 做完整可见语义等价校验。
保留不可变发送卡 digest,以及消息、群、机器人、动作 digest、成员身份和授权主体的精确校验。
在上述独立绑定均通过后,兼容受限的旧版标题/占位符读回。
回放时重建不可变确认投影,保证幂等且不重复执行。
暴露稳定、可公开的回调失败码,避免让用户盲目重复点击。
Entry points / 入口
Validation / 验证
main.Live group-click qualification is intentionally still pending and will be recorded before merge; no real broker, signature, transfer, or order path is involved.