Repository navigation
Lark manager: decouple context capture from Turn authority #4318
Description
Problem / 问题
The manager route currently resolves decide_manager_event() before creating or ingesting its runtime inbox event. Any group message that is in the configured manager chat but lacks a provider-native mention or a verified reply is returned as not_addressed and disappears from the manager's durable context.
管家路由目前先执行 decide_manager_event(),之后才创建并写入运行时 inbox。因此,位于已配置管家群但缺少 provider 原生 mention 或 verified reply 的消息会以 not_addressed 被丢弃,不会进入管家的持久上下文。
This is observable with rich-text messages whose rendered text contains @ but whose provider payload contains no matching at node. The strict authority check is correct, but using it as the capture gate is too brittle: visibility and execution authority are different decisions.
富文本消息可能显示 @,但 provider payload 中并没有匹配的 at 节点。严格的权限判断是正确的,但不应同时充当采集开关:可见性与执行权限应是两个不同决策。
Proposed contract / 建议契约
-
For one unambiguous, enabled manager chat binding, persist bounded non-self group messages as context-only material, even when they are not addressed to the bot.
-
Only provider-native mention, verified reply-to-bot, or another existing typed authority record may start/steer a manager Turn or mutate Goal/Todo state.
-
A later authorized manager Turn may read a bounded recent context window, with every item explicitly marked non-authoritative. It must not reinterpret those items as standalone commands.
-
Context-only items require an idempotent review/consumption receipt. They must not trigger a Lark reply, reaction, source ACK, or model call on their own.
-
Preserve target/chat/Goal isolation, self-message filtering, stable ordering, restart recovery, duplicate delivery safety, and content-free health projections.
-
对唯一且启用的管家群绑定,持久化有界的非机器人群消息为 仅上下文材料,即使消息没有明确 @ 机器人。
-
只有 provider 原生 mention、已验证的 reply-to-bot 或其他既有 typed authority 记录,才能启动/steer 管家 Turn 或修改 Goal/Todo。
-
后续获得授权的管家 Turn 可以读取有界的近期上下文窗口,但每条材料必须明确标记为非权威,且不得被重新解释成独立指令。
-
仅上下文材料需要幂等 review/consumption receipt;它自身不得触发 Lark 回复、reaction、source ACK 或模型调用。
-
保持 target/chat/Goal 隔离、自消息过滤、稳定排序、重启恢复、重复投递安全和无正文的健康投影。
Acceptance / 验收
-
An unaddressed message in the configured manager chat is durably captured and reported as
context_only_captured;answer, reply, reaction and source ACK are not called. -
A subsequent native mention receives a bounded prior-context packet plus the addressed message; only the addressed message grants Turn authority.
-
A message in another chat, a self message, or an ambiguous manager binding remains rejected and is not captured.
-
Duplicate events and restart recovery do not duplicate context or replies.
-
CLI/managed Turn, frontend and Lark expose the same typed capture/authority distinction and readback; no second configuration source is introduced.
-
Documentation and contract changes are bilingual.
-
已配置管家群中的未寻址消息会被持久化,并返回
context_only_captured;不得调用answer、回复、reaction 或 source ACK。 -
后续原生 mention 会收到有界历史上下文和当前寻址消息;只有当前寻址消息授予 Turn 权限。
-
其他群、自消息或多重歧义的管家绑定仍应拒绝且不采集。
-
重复事件与重启恢复不得产生重复上下文或重复回复。
-
CLI/managed Turn、frontend 与 Lark 展示同一份 typed capture/authority 区分与回读,不新增第二配置源。
-
文档与契约默认双语。
Boundaries / 边界
This issue does not authorize ordinary group chat to mutate LoopX, does not weaken native mention/reply verification, and does not replay historical messages automatically.
本问题不授权普通群聊修改 LoopX,不降低原生 mention/reply 校验,也不自动重放历史消息。
Activity
hahahahahayesyeseys commentedon Sep 22, 2026
Verified on current
main: the contract described here is implemented, and the acceptance cases are pinned by tests. This looks like an issue left open after its PR merged rather than open work.Implemented by #4319 (
fix(lark): preserve context without granting Turn authority, merged 2026-09-14, merge commita55702c25).manager_routing.pyreturnscapture_scope: configured_chat_allwithauthority_modeturn_authorizedorcontext_only, and the comment at the return states the splittest_lark_goal_topic_connections.py:2717-2718asserts both fields for an unaddressed messageturn_authorized = is_event_addressed_to_bot(...)gates the mode; a rich-text@without a provideratnode yieldscontext_onlygoal_topic_runtime.py:907-913reportscontext_only_capturedtest_lark_goal_topic_runtime.py:373asserts that status, and the harness'sanswerspy (same file, lines 344-354, 478) is what proves no reply is emittedcontext_only_already_capturedis the idempotent second resulttest_lark_goal_topic_runtime.py:394historical_context_onlythreaded throughturn_start_sync.pyandevent_inbox.pytest_lark_turn_start_sync.py:466-508route_ambiguous/self_messagereturn before any authority mode is formedThe dashboard already carries the matching control (
接收范围/ capture scope) rather than introducing a second configuration source, which is the last boundary this issue asks for.One point I did not verify independently, so I am not claiming it: whether acceptance bullet 3's bounded window has its own test -- I confirmed the context mechanism exists and is marked non-authoritative, but did not check that an over-long window is bounded by a named limit. Worth a look if this is reopened; otherwise it reads as ready to close.
test(lark): pin the manager context window's bounds and order (#4900)