fix(lark): preserve context without granting Turn authority - #4319
Conversation
1428654 to
18c633c
Compare
|
CI found the post-rebase goal_topic_runtime module over its 1500-line maintainability budget. I moved bounded manager context selection, prompt composition, history sync, and context settlement into a dedicated manager_context owner module without changing authority semantics. Validation on exact head e96d2fd:
The branch remains review-required because it changes manager/Lark authority behavior. |
huangruiteng
left a comment
There was a problem hiding this comment.
English verdict: REQUEST_CHANGES — the capture/authority split is well scoped, but historical Bot mentions remain actionable and cannot be settled by the new context-only path.
动机
本次 review 针对精确 head e96d2fd9c6c01145a613458bb2ac593e37f80a73(base main)进行。PR 修复了 #4318 的核心问题:在唯一且有效的 Manager 群绑定下,将可见性与 Turn 权限拆开,允许未寻址群消息进入本地私有 inbox,只有 provider-native mention 或 verified reply 才能启动 Manager Turn。manager_context 的 8 条/4,000 字符边界、历史补采、无副作用 context-only 分支,以及 dashboard 状态都与目标一致。
不过“历史补采一律 context-only,包括旧的真实 mention”是本 PR 的强语义承诺,不能只靠 prompt 文案或一个额外布尔字段表达。当前 event inbox 仍把历史 mention 视为 addressed,导致 material-review 结算拒绝、健康/注意力投影显示 reply_due,并在后续授权 Turn 中重复注入。这个缺口会让一个明确禁止 replay 的协议在共享消费者处重新变成 actionable,因此结论为 REQUEST_CHANGES。
改动思路
主路径是 poll_lark_goal_topic_profile_once → process_lark_goal_topic_event。manager_routing.decide_manager_event 先验证唯一 manager binding、chat、self-message 和 connector 状态,再将非寻址事件标为 authority_mode=context_only,将 provider-native mention/verified reply 标为 turn_authorized。runtime 对 context-only 事件只调用 ingest_lark_event_inbox 并返回,不调用模型、reply、reaction 或 source ACK;授权事件则在回答前调用 sync_manager_context,用同一 config/cursor/inbox 补采历史,取最近 8 条/4,000 字符,构造带明确边界的 Manager prompt,成功回复后通过既有 material-review ledger 结算,再 ACK 当前事件。
这个拆分复用了现有 inbox、reaction/read receipt、delivery receipt 和 dashboard projection,架构方向正确。问题在于历史状态的 owner 分裂:turn_start_sync 写入 historical_context_only=True,manager_context_materials 会据此纳入材料,但 event_inbox._event_attention_kind/通用 operator_inbox_attention_kind 只看 addressed_to_bot、reply 和 mentions,settle_lark_event_inbox_material_review 也沿用同一判断。历史 provenance 没有在共享 authority/attention boundary 形成优先级规则。
具体改动
关键代码讲解
decide_manager_event(loopx/extensions/lark/manager_routing.py:87-116)把原先not_addressed的 Manager 事件改为context_only,并固定configured_chat_all;只有is_event_addressed_to_bot为真时才保留 Turn authority。process_lark_goal_topic_event(loopx/extensions/lark/goal_topic_runtime.py:1067-1087,1088-1163)先持久化事件,context-only 立即返回;授权事件执行 history sync、上下文选择和后续回复/ACK。settle_manager_context在成功 Turn 后为每条材料写 material-review receipt。manager_context_materials(loopx/extensions/lark/manager_context.py:21-61)排除当前/普通 live addressed 事件,保留历史 context-only,按最近 8 条和总计 4,000 字符截断,并由manager_message显式标注“不构成指令、授权或独立待办”。sync_lark_turn_start_inbox(loopx/extensions/lark/turn_start_sync.py:539-555,823-856)在补采事件上写入historical_context_only=True,关闭历史 reaction,并复用私有 cursor/inbox。_event_from_payload(loopx/extensions/lark/event_inbox.py:314-334)目前是在计算addressed_to_bot=True后再保留 historical flag;这是下面 P1 的根因。共享_event_attention_kind(446-466)和 material-review 校验没有消费该 flag。
对主干的风险
P1 — 历史真实 mention 没有真正降级为 context-only,导致结算失败并产生 reply_due。
触发:turn-start history 收到一个旧的 provider-native Bot mention,写入 historical_context_only=True。路径是 _event_from_payload 保留 addressed_to_bot=True/mention metadata → manager_context_materials 纳入 → settle_manager_context 调用 settle_lark_event_inbox_material_review → _event_attention_kind 仍判为 addressed。实际结论是抛出“addressed Lark events must use the reply_due settlement path”;settle_manager_context 捕获并计数为 0,材料继续 pending。独立 urgency 复现还得到 direct_question=1、reply_due=True,这会把明确禁止 replay 的历史材料暴露给通用 attention/reply lane。下一次授权 Turn 会再次注入同一材料。
最小修复:让 historical_context_only 在共享 attention classifier 和 material-review settlement 中具有明确的最高优先级(或在 normalization 时把历史记录的 addressing flags 置为 false,同时把原始 mention 保存为独立 provenance 字段)。加入端到端回归:补采真实 mention,断言 reply_due=False/material-only,授权 Turn 后 settle_manager_context 成功写 ledger 与 processed readback,并覆盖重复投递与重启。不要只把 test 的 addressed_to_bot=True 继续作为预期而不验证下游消费者。
P2 — context-only inbox 没有持久化 retention 上限。
MANAGER_CONTEXT_ITEM_LIMIT=8 与 drain_limit=8 只限制一次 prompt/排水,不限制 .loopx/inbox 中已捕获且等待未来授权 Turn 的文件数量。若群里持续有非寻址消息但很少出现授权 mention,这些事件不会 source ACK/settle,会无限累积并把过期内容留在后续上下文候选中。建议补充有界 retention/expiry/compaction 及可观察的丢弃原因;该问题不改变当前 P1 结论,但应在同一 inbox owner 内处理。
验证结果:uv run --with pytest pytest -q tests/extensions/test_lark_goal_topic_runtime.py tests/extensions/test_lark_goal_topic_connections.py tests/extensions/test_lark_turn_start_sync.py 为 154 passed;Python compileall passed。远端精确 head 为 MERGEABLE、mergeStateStatus=BLOCKED,26 个 checks 成功;本地 ruff 不可用,未重复 lint。P1 由真实 event_inbox/urgency/settlement 边界复现,不是远端环境噪声。
我的整体评价
Manager capture/authority split、history cursor 复用、无副作用 context-only 分支和前端状态是有价值且范围合适的实现。live unaddressed capture 与后续 authorized prompt 的正向行为已有良好覆盖;但历史状态只在新 helper 中被识别,在共享 attention/settlement owner 中仍会恢复为 direct attention,形成 observable drift。请先修复历史 precedence、补齐 ledger/urgency/restart 回归,再在新的 exact head 上重跑远端 checks 和真实 Lark/browser readback;修复前不建议批准。
e96d2fd to
db4bec3
Compare
RFC reconciliation at exact head db4bec3Rebased onto main containing #4330 and reframed this PR as an early M3/A10 Lark Observation adapter, not a collaboration request owner. The blocking historical-mention drift is fixed: provider addressing remains historical provenance, while live addressing/reply flags are cleared. The generic urgency projector and Lark material-review settlement now both keep a recovered native mention material-only. The end-to-end regression proves reply_due=false, no attention lane, successful settlement, and zero pending context after the authorized Turn. Validation: 154 Lark runtime/connection/turn-start tests passed; maintainability ratchet 8 passed; Ruff and diff hygiene passed. One gate remains explicit: 8 items / 4,000 characters bounds a Turn projection, not durable content retention. Retention/expiry/compaction with an observable discard reason and duplicate/restart safety is still required before long-running M3/A10 acceptance. Authority behavior and this gate keep the PR review-required; it is not self-merged. |
db4bec3 to
75d1524
Compare
|
Post-#4344 rebase receipt / #4344 合入后回执:base f4d7c9d, exact head 75d1524. Local evidence: 154 Lark tests, 8 maintainability tests, Ruff/diff, dashboard/chat production build and packaged personal-workspace browser smoke all pass. RFC scope remains early M3/A10 Observation adapter; durable retention/expiry/compaction is still a gate. Independent Lark-authority review is required. |
50998fe to
a5fbb99
Compare
60117c0 to
50cef49
Compare
There was a problem hiding this comment.
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
动机
本 PR 的目标是让已绑定的 Lark 管家群保留有用上下文,同时严格区分“被捕获的背景材料”和“可以驱动一次 Manager Turn 的授权消息”。此前实现虽然限制了单次 prompt 的条数和字符数,但 pending inbox 本身没有留存/过期边界;回复传输在崩溃重试后也可能重新选择更新的材料。另一个风险是 authority_mode 以裸字符串进入运行时,缺失或未知值可能落入授权分支。当前修复覆盖了这三个可复现的边界,并保持 M2 协作生命周期不在本 PR 范围内。
改动思路
入口仍是 process_lark_goal_topic_event,复用现有 Lark inbox、material-review、delivery receipt、cursor 和 ACK 所有权。manager_routing 只产生两个明确的 ManagerAuthorityMode 值;运行时对缺失/未知值 fail-closed。Manager 上下文由 manager_context 统一负责:先从完整 pending projection 做 7 天/32 条留存整理,再取最多 8 条、总计 4,000 字符的非权威材料。发送前把精确 message id 集合写入私有 delivery receipt;重试只恢复这组 id,材料不可用时保持当前事件 pending,绝不静默替换为新到材料。留存整理通过既有 material-review settlement 完成,并额外写入带 retention_expired/retention_overflow 原因的私有 receipt,便于观测和恢复。
具体改动
manager_routing.py新增ManagerAuthorityMode与严格解析函数,保留现有 provider mention/reply 授权来源。goal_topic_runtime.py在任何 Manager 副作用前校验模式;接入留存 projection、精确上下文恢复、delivery receipt 的context_material_ids,原有 reply verification、effect ACK 和 current-event ACK 顺序不变。manager_context.py抽出候选过滤/有序裁剪、7 天/32 条 retention/compaction、保护未完成 receipt 引用、可观测丢弃原因,以及重试时的 exact-id 恢复。manager_reply_delivery.py对context_material_ids做 message-id 格式、唯一性和 tamper 校验;旧 receipt 缺少该字段时仍可读取。event_inbox.py为内部 retention/retry caller 提供完整 projection 读取(limit=0),不改变默认公开 1..100 上限。- 测试覆盖未知/缺失模式、40 条 churn、过期/溢出 receipt、更新材料后的 exact-id retry、历史 mention 归一化和传输失败;文档与已打包 chat 资源同步于原 PR。
关键代码讲解
ManagerAuthorityMode/parse_manager_authority_mode:只接受context_only或turn_authorized,未知状态在进入 reaction、model、reply 之前返回invalid_manager_authority_mode。compact_manager_context/manager_context_projection:按时间和稳定 message id 排序,先处理七天外材料,再处理超过 32 条的最旧材料;每次 settlement 都留下 material-review 与 retention 原因记录,并保护 pending/sent receipt 正在引用的 id。pending_delivery/restore_manager_context_route:发送前固化context_material_ids,重试按 receipt 恢复全部原材料;缺失任何 id 返回context_materials_unavailable、不 ACK 当前事件。
对主干的风险
这是行为型变更,影响只限于启用 Manager binding 的 Lark inbox;非 Manager、direct/async Goal Topic 和默认 disabled 路径保持原逻辑。留存 settlement 复用已有幂等 ledger,失败仍会保留 pending 状态。上下文 retention 是本地私有状态,不扩大通用 inbox/工作车道契约;prompt 中的 [context-only] 仍是指导性标记,真正的授权和留存边界由枚举、receipt 和 compaction 机器执行。完整 diff 的生产热点仍由 manager_context 承载,goal_topic_runtime.py 保持在 1,500 行 ratchet 上限。当前剩余风险仅是真实 Lark provider 的网络/权限/分页行为需要远端环境验证,仓库测试不把 mock 结果当作外部可用性证明。
我的整体评价
我已按最新主干 9e2b6d425fff9b8b2ebd69a79c3d8491b12eb7b3 重新审阅整个 PR,当前 exact head 为 620d93bac531f3b19b0790a321f1839082421dc0。此前评审提出的 retention/expiry/compaction、exact selected-id retry 和 authority fail-open 三个问题均已由实现和回归测试闭环;没有遗留 blocking finding。145 个聚焦测试、ruff、py_compile、维护性 ratchet、公共边界扫描及标准 premerge canary(18 checks、0 failures)通过;GitHub 仓库必需 checks(含 DCO、构建、兼容性、测试分片、Stage2C、merge-gate)也已全部通过。独立 SonarCloud Code Analysis 状态是非阻塞的质量门禁失败,已单独记录,不影响仓库 merge-gate;我建议 APPROVE,并在作者自评审限制下以公开 COMMENTED 结论记录该判断。
English verdict: APPROVE for exact head 620d93bac531f3b19b0790a321f1839082421dc0; retention/expiry, exact context-id retry, and fail-closed authority validation are implemented and covered by 145 focused tests, the standard premerge canary, and all repository-required checks. The separate SonarCloud quality-gate status is non-blocking and failed; repository merge-gate passed. GitHub blocks formal self-approval for the PR author, so this is recorded as the author-owned approval conclusion in a COMMENTED review.
a61e961 to
620d93b
Compare
huangruiteng
left a comment
There was a problem hiding this comment.
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
动机
本 PR 的目标是让已绑定的 Lark 管家群保留有用上下文,同时严格区分“被捕获的背景材料”和“可以驱动一次 Manager Turn 的授权消息”。此前实现虽然限制了单次 prompt 的条数和字符数,但 pending inbox 本身没有留存/过期边界;回复传输在崩溃重试后也可能重新选择更新的材料。另一个风险是 authority_mode 以裸字符串进入运行时,缺失或未知值可能落入授权分支。当前修复覆盖了这三个可复现的边界,并保持 M2 协作生命周期不在本 PR 范围内。
改动思路
入口仍是 process_lark_goal_topic_event,复用现有 Lark inbox、material-review、delivery receipt、cursor 和 ACK 所有权。manager_routing 只产生两个明确的 ManagerAuthorityMode 值;运行时对缺失/未知值 fail-closed。Manager 上下文由 manager_context 统一负责:先从完整 pending projection 做 7 天/32 条留存整理,再取最多 8 条、总计 4,000 字符的非权威材料。发送前把精确 message id 集合写入私有 delivery receipt;重试只恢复这组 id,材料不可用时保持当前事件 pending,绝不静默替换为新到材料。留存整理通过既有 material-review settlement 完成,并额外写入带 retention_expired/retention_overflow 原因的私有 receipt,便于观测和恢复。
具体改动
manager_routing.py新增ManagerAuthorityMode与严格解析函数,保留现有 provider mention/reply 授权来源。goal_topic_runtime.py在任何 Manager 副作用前校验模式;接入留存 projection、精确上下文恢复、delivery receipt 的context_material_ids,原有 reply verification、effect ACK 和 current-event ACK 顺序不变。manager_context.py抽出候选过滤/有序裁剪、7 天/32 条 retention/compaction、保护未完成 receipt 引用、可观测丢弃原因,以及重试时的 exact-id 恢复。manager_reply_delivery.py对context_material_ids做 message-id 格式、唯一性和 tamper 校验;旧 receipt 缺少该字段时仍可读取。event_inbox.py为内部 retention/retry caller 提供完整 projection 读取(limit=0),不改变默认公开 1..100 上限。- 测试覆盖未知/缺失模式、40 条 churn、过期/溢出 receipt、更新材料后的 exact-id retry、历史 mention 归一化和传输失败;文档与已打包 chat 资源同步于原 PR。
关键代码讲解
ManagerAuthorityMode/parse_manager_authority_mode:只接受context_only或turn_authorized,未知状态在进入 reaction、model、reply 之前返回invalid_manager_authority_mode。compact_manager_context/manager_context_projection:按时间和稳定 message id 排序,先处理七天外材料,再处理超过 32 条的最旧材料;每次 settlement 都留下 material-review 与 retention 原因记录,并保护 pending/sent receipt 正在引用的 id。pending_delivery/restore_manager_context_route:发送前固化context_material_ids,重试按 receipt 恢复全部原材料;缺失任何 id 返回context_materials_unavailable、不 ACK 当前事件。
对主干的风险
这是行为型变更,影响只限于启用 Manager binding 的 Lark inbox;非 Manager、direct/async Goal Topic 和默认 disabled 路径保持原逻辑。留存 settlement 复用已有幂等 ledger,失败仍会保留 pending 状态。上下文 retention 是本地私有状态,不扩大通用 inbox/工作车道契约;prompt 中的 [context-only] 仍是指导性标记,真正的授权和留存边界由枚举、receipt 和 compaction 机器执行。完整 diff 的生产热点仍由 manager_context 承载,goal_topic_runtime.py 保持在 1,500 行 ratchet 上限。当前剩余风险仅是真实 Lark provider 的网络/权限/分页行为需要远端环境验证,仓库测试不把 mock 结果当作外部可用性证明。
我的整体评价
我已按最新主干 9e2b6d425fff9b8b2ebd69a79c3d8491b12eb7b3 重新审阅整个 PR,当前 exact head 为 620d93bac531f3b19b0790a321f1839082421dc0。此前评审提出的 retention/expiry/compaction、exact selected-id retry 和 authority fail-open 三个问题均已由实现和回归测试闭环;没有遗留 blocking finding。145 个聚焦测试、ruff、py_compile、维护性 ratchet、公共边界扫描及标准 premerge canary(18 checks、0 failures)通过;GitHub 仓库必需 checks(含 DCO、构建、兼容性、测试分片、Stage2C、merge-gate)也已全部通过。独立 SonarCloud Code Analysis 状态是非阻塞的质量门禁失败,已单独记录,不影响仓库 merge-gate;我建议 APPROVE,并在作者自评审限制下以公开 COMMENTED 结论记录该判断。
English verdict: APPROVE for exact head 620d93bac531f3b19b0790a321f1839082421dc0; retention/expiry, exact context-id retry, and fail-closed authority validation are implemented and covered by 145 focused tests, the standard premerge canary, and all repository-required checks. The separate SonarCloud quality-gate status is non-blocking and failed; repository merge-gate passed. GitHub blocks formal self-approval for the PR author, so this is recorded as the author-owned approval conclusion in a COMMENTED review.
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>
620d93b to
d2e8b59
Compare
huangruiteng
left a comment
There was a problem hiding this comment.
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
动机
本 PR 的目标是让已绑定的 Lark 管家群保留有用上下文,同时严格区分“被捕获的背景材料”和“可以驱动一次 Manager Turn 的授权消息”。此前实现虽然限制了单次 prompt 的条数和字符数,但 pending inbox 本身没有留存/过期边界;回复传输在崩溃重试后也可能重新选择更新的材料。另一个风险是 authority_mode 以裸字符串进入运行时,缺失或未知值可能落入授权分支。当前修复覆盖了这三个可复现的边界,并保持 M2 协作生命周期不在本 PR 范围内。
改动思路
入口仍是 process_lark_goal_topic_event,复用现有 Lark inbox、material-review、delivery receipt、cursor 和 ACK 所有权。manager_routing 只产生两个明确的 ManagerAuthorityMode 值;运行时对缺失/未知值 fail-closed。Manager 上下文由 manager_context 统一负责:先从完整 pending projection 做 7 天/32 条留存整理,再取最多 8 条、总计 4,000 字符的非权威材料。发送前把精确 message id 集合写入私有 delivery receipt;重试只恢复这组 id,材料不可用时保持当前事件 pending,绝不静默替换为新到材料。留存整理通过既有 material-review settlement 完成,并额外写入带 retention_expired/retention_overflow 原因的私有 receipt,便于观测和恢复。
具体改动
manager_routing.py新增ManagerAuthorityMode与严格解析函数,保留现有 provider mention/reply 授权来源。goal_topic_runtime.py在任何 Manager 副作用前校验模式;接入留存 projection、精确上下文恢复、delivery receipt 的context_material_ids,原有 reply verification、effect ACK 和 current-event ACK 顺序不变。manager_context.py抽出候选过滤/有序裁剪、7 天/32 条 retention/compaction、保护未完成 receipt 引用、可观测丢弃原因,以及重试时的 exact-id 恢复。manager_reply_delivery.py对context_material_ids做 message-id 格式、唯一性和 tamper 校验;旧 receipt 缺少该字段时仍可读取。event_inbox.py为内部 retention/retry caller 提供完整 projection 读取(limit=0),不改变默认公开 1..100 上限。- 测试覆盖未知/缺失模式、40 条 churn、过期/溢出 receipt、更新材料后的 exact-id retry、历史 mention 归一化和传输失败;文档与已打包 chat 资源同步于原 PR。
关键代码讲解
ManagerAuthorityMode/parse_manager_authority_mode:只接受context_only或turn_authorized,未知状态在进入 reaction、model、reply 之前返回invalid_manager_authority_mode。compact_manager_context/manager_context_projection:按时间和稳定 message id 排序,先处理七天外材料,再处理超过 32 条的最旧材料;每次 settlement 都留下 material-review 与 retention 原因记录,并保护 pending/sent receipt 正在引用的 id。pending_delivery/restore_manager_context_route:发送前固化context_material_ids,重试按 receipt 恢复全部原材料;缺失任何 id 返回context_materials_unavailable、不 ACK 当前事件。
对主干的风险
这是行为型变更,影响只限于启用 Manager binding 的 Lark inbox;非 Manager、direct/async Goal Topic 和默认 disabled 路径保持原逻辑。留存 settlement 复用已有幂等 ledger,失败仍会保留 pending 状态。上下文 retention 是本地私有状态,不扩大通用 inbox/工作车道契约;prompt 中的 [context-only] 仍是指导性标记,真正的授权和留存边界由枚举、receipt 和 compaction 机器执行。完整 diff 的生产热点仍由 manager_context 承载,goal_topic_runtime.py 保持在 1,500 行 ratchet 上限。当前剩余风险仅是真实 Lark provider 的网络/权限/分页行为需要远端环境验证,仓库测试不把 mock 结果当作外部可用性证明。
我的整体评价
我已按最新主干 49f979b06c38b754cd6aab4a63dfd6bcc846e713 重新审阅整个 PR,当前 exact head 为 d2e8b599048a127a5fe117b67c34d7bab6ad9eea。此前评审提出的 retention/expiry/compaction、exact selected-id retry 和 authority fail-open 三个问题均已由实现和回归测试闭环;没有遗留 blocking finding。145 个聚焦测试、ruff、py_compile、维护性 ratchet、公共边界扫描及标准 premerge canary(18 checks、0 failures)通过;GitHub 仓库必需 checks(含 DCO、构建、兼容性、测试分片、Stage2C、merge-gate)也已全部通过。独立 SonarCloud Code Analysis 状态是非阻塞的质量门禁失败,已单独记录,不影响仓库 merge-gate;我建议 APPROVE,并在作者自评审限制下以公开 COMMENTED 结论记录该判断。
English verdict: APPROVE for exact head d2e8b599048a127a5fe117b67c34d7bab6ad9eea; retention/expiry, exact context-id retry, and fail-closed authority validation are implemented and covered by 145 focused tests, the standard premerge canary, and all repository-required checks. The separate SonarCloud quality-gate status is non-blocking and failed; repository merge-gate passed. GitHub blocks formal self-approval for the PR author, so this is recorded as the author-owned approval conclusion in a COMMENTED review.
Motivation / 动机
A configured Manager group could not provide useful conversation context because unaddressed messages were rejected before entering the private runtime inbox. At the same time, recovering old history must never replay an earlier Bot mention as a new authorized Turn.
已配置的管家群此前会在消息进入私有 runtime inbox 前拒绝未寻址内容,导致群聊上下文不完整;但补采历史也绝不能把旧的真实 mention 重放为新 Turn。
RFC alignment / RFC 对齐
This is an early M3 Lark adapter slice under the merged capable-manager semantic-handoff RFC (#4330), with A10 as the eventual journey anchor.
本 PR 只把 Lark 可见上下文收窄成 M3 adapter 行为;不实现 M2 request/assessment/result owner,也不把一条群消息或 Codex 消息冒充已交付工作。
Behavior / 行为
Delivery receipt / 交付回执
Validation / 验证
This changes Lark authority behavior and retains an open retention gate. It remains review-required and is not a self-merge candidate.