Skip to content

fix(lark): isolate Agent Goal Channel binding resolution - #3992

Merged
huangruiteng merged 1 commit into
mainfrom
codex/goal-channel-weekly-report-trigger
Sep 6, 2026
Merged

huangruiteng merged 1 commit into
mainfrom
codex/goal-channel-weekly-report-trigger

Conversation

@huangruiteng

@huangruiteng huangruiteng commented Sep 6, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • resolve the selected Goal Channel connection and Agent before comparing provider targets;
  • prevent a sibling Agent with a different target from hiding the addressed Agent's valid binding;
  • preserve the latest deterministic invalid-default fallback from main;
  • add focused regression coverage for multi-Agent binding isolation.

Scope

This PR is intentionally limited to the generic Goal Channel binding fix. It does not classify report requests, scan inbox messages, create periodic-report intents, or add report-delivery behavior.

The Agent-authorized provider-neutral report action and manifest-discovered adapter are split into follow-up PR #4001.

Validation

  • Ruff passed for the changed Python files;
  • 118 focused Lark Goal Channel and Topic tests passed;
  • loopx canary premerge --from-git-diff --git-diff-base origin/main --goal-id loopx-meta: 9/9 checks passed;
  • strict change-quality receipt: cqr_ea034623f4c57841068b;
  • exact diff fingerprint: ea034623f4c57841068b04d0e8145e2cee05ee8e51dfd9f3424f1efd7bc2f4ad;
  • git diff --check origin/main...HEAD passed.

No real Lark message was sent. This PR will wait for independent review and will not be self-merged.

@huangruiteng huangruiteng 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.

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

评审 head:c67354f47a12b5cf05d681dd82a0e045b9421314。结论:Request changes;不是合并许可。

动机

原有 periodic-report 已有阶段完成触发、编辑输入、冻结产物、投递 Todo 和发布链路,但在 Goal Channel 中对某个 Agent 说“请生成周报”没有接到这条链路。本 PR 增加手动入口,减少用户切换到 CLI 的成本。这个方向有价值,但它是 Goal Channel 报告入口,不是新增 Personal Workspace 前端交互,不应仅为了 v1.0 发布赶进主干。

改动思路

Lark extension 保留原始消息及 provider 身份证据,只向 capability 返回带 Goal/Agent、请求摘要和 binding revision 的中立 observation。quota 注入 reader,将请求投影为 governed capability intent;consume-pending 复用现有 editorial、generation bundle、Workspace projection、publication candidate 和 delivery Todo。只有持久化 delivery_ready receipt 后才 ACK,ACK 失败时复用 receipt 重试,不重复建 Todo。

正向路径已通过现有 CLI 集成测试:单一原生 mention → request → quota governed action → editorial_required(不 ACK)→ 编辑响应 → delivery_ready + 一个投递 Todo → ACK 后请求消失。consume 本身没有执行远端发送;实际发送仍由已有投递链路负责。关闭订阅时 capability 不调用 reader,Lark reader/settler 也通过 drain/ack activation 门禁;安装或发现 provider 不等于授权。

具体改动

  • periodic_report_request.py:_resolved_request_context 绑定注册 Agent、Goal Channel、target、inbox;_explicit_weekly_report_request 判定自然语言;read/settle 负责 observation 和 ACK,新增生产模块 423 行。
  • event_inbox.py、group_history.py、goal_topic_runtime.py:传递 sender_type、原生 mention 数量及 addressing_source,保留消息来源判断所需证据;原始文本不进入 capability observation。
  • pending_intent.py:_manual_request_intent 复用 preset/trigger;合并 provider 请求与已有 sidecar;consume 复用已有冻结产物和投递 Todo,增加 settlement-only retry。
  • capability post_writeback_hook.py 校验 manual request schema/摘要/actor/addressing,复用既有 trigger 评估;capability cli.py、顶层 cli.py、cli_commands/lark_inbox.py、quota.py 注入 reader/settler,确有生产调用路径,不是测试专用模块。
  • 共享 cli_commands/post_writeback.py 新增 identity 不完整时的 neutral skip:不调用 hook、不产生 failure。这是独立的共享行为变化,需要单独评估与报告触发的必要关系。
  • protocol 文档描述新入口和边界;四个测试文件覆盖 provider 路由、CLI 全链路、消费重试、identity skip 和 sender type。

总计 16 文件、+1919/-40:生产 +897/-39,测试 +984/-1,文档 +38。复用既有产物/投递链路是正确的;最高价值简化不是重建另一套报告服务,而是缩小入口识别和 binding 解析责任,并拆出不必要的共享 dispatcher 行为变更。

对主干的风险

[P1] 讨论/询问被升级成报告生成请求

位置:loopx/extensions/lark/periodic_report_request.py 的 _REPORT_ACTION 与 _explicit_weekly_report_request(约 43–86 行)。当前规则是任意位置命中“周报”与“写”等动作子串,再排除少量否定/完成问句。

独立通过真实 ingest → read 路径复现:带单一原生 Agent mention 的“我写过周报,想讨论一下格式”和“how to write a weekly report?” 均返回 manual request,而应保持普通讨论。它不是单纯文案误判:现有下游会把该 observation 提升为 governed action,要求 consume,再进入已有订阅授权的生成/投递链路。不能把“消息确实对这个 Bot 说”当成“用户要求生成报告”。

最小修复:对明确命令建立收敛的完整意图解析结果(例如 request/discussion/ambiguous);无法确认则不升级。不要继续累加零散 substring 黑名单。加入这两个反例、明确中文/英文命令正例和取消反例,验证讨论不会产生 pending intent、生成或 ACK。

[P2] 另一个 Agent 的不同 target 使当前 Agent 的有效请求不可见

位置:loopx/extensions/lark/periodic_report_request.py:169 的 binding_for_goal(... provider_target=target, connection_id=...)。

独立反例先验证只有 alpha connection 时返回一个有效请求;再加入注册的 beta Agent connection 和单独的 beta provider target,alpha 原请求读取抛出 Goal Channel provider target does not match target_ref。原因是现有 binding_for_goal 先对所有 sibling connection 用 alpha target 执行解析,再按 connection_id 筛选。上层 pending_periodic_report_intents 捕获 ValueError 后将 provider observations 置空,使正确请求在 quota 中静默消失;beta 不应影响 alpha。

最小修复:先按明确 connection 选定原始 binding,再用该 binding 的 target 解析;复用已有精确选择思路,不要把单个 target 传给全连接集合。加入不同 target 的双 Agent 正例,以及当前 Agent 自身配置错误仍拒绝的反例。#3983 的 reconnect root 路径已经暴露过同类解析边界,但其局部修复不会自动覆盖本入口。

验证与剩余边界

本地在上述 exact head 跑四个改动测试模块:83 passed;另加 3 个独立语义探针:3 failed,分别对应上述两个问题。远端该 head 的 Python shards/aggregate、Windows、checks、build、DCO、dependency、Sonar 已成功;发布专用任务跳过。绿色 CI 没有覆盖这两个反例。

尚未独立完成所有共享面的 pre-change/disabled/enabled 配对验证:尤其 event inbox 新增持久字段和 identity-incomplete dispatcher 返回值,不能仅凭关闭订阅后没有报告来声称 default-off 全隔离。此项仍是 unverified,批准前应补齐或把共享行为拆出明确披露;作者声明的完整 canary/TS 检查不冒充本次独立执行。未执行真实 Lark 外发,也未进行无授权的线上验证。

我的整体评价

复用已有报告链路、内容无关 observation、持久回执后 ACK 和 ACK-only retry 都是正向设计;但入口现在把讨论当命令,又会被别的 Agent 的 target 阻断,未达到 PR 声明的明确请求与 Agent 隔离条件。建议先修这两个边界,再补齐共享 default-off 配对验证。改动比例仍需结合共享 dispatcher 拆分重新判断,当前不批准,不建议作为个人工作区 v1.0 的抢合并项。

English verdict: REQUEST_CHANGES at c67354f. The existing report pipeline is reused well, but addressed discussion is promoted to a manual generation request, and an unrelated Agent connection with another target breaks the valid current-Agent request. 83 focused tests pass; three independent semantic probes fail. Remote CI is green, but full shared-surface default-off counterfactual validation remains unverified. No merge performed.

@huangruiteng

Copy link
Copy Markdown
Collaborator Author

Implementation update at d3ea96ef95b0b879f5807501d11364231eea766c

The two concrete findings from the review at c67354f47a12b5cf05d681dd82a0e045b9421314 are addressed:

  • P1, discussion promoted to generation: request classification is now fail-closed for discussion and how-to phrasing, with explicit Chinese and English command positives plus discussion, ambiguity, cancellation, and status-only negatives. The downstream integration coverage proves rejected text produces no pending intent and no ACK.
  • P2, sibling target hides a valid request: binding lookup now selects the addressed connection before validating its target. Focused coverage includes two Agents with different targets and preserves rejection for a misconfigured target on the addressed Agent itself.

The companion architecture pass also removes the composition-root coupling called out afterward. Lark v1.6.0 declares periodic-report reader and settler factories through manifest [[hook_adapters]]; generic extension runtime discovers activated adapters, and capability composition exposes provider-neutral ports. Quota, scheduler follow-up, the top-level CLI, Lark inbox commands, and consume-pending no longer import or construct Lark reader/settler implementations. No TypeScript kernel or loopx/control_plane/ file changed.

Product/architecture judgment: the user-facing Goal Channel request now reaches the existing governed report pipeline without creating a second report authority. Provider identity and inbox settlement stay extension-owned; lifecycle and permission gates stay in generic extension discovery; report policy and durable delivery remain capability-owned. This is a reusable plugin contract rather than a Lark special case.

Validation on the exact 23-file diff:

  • 247 focused Python tests passed;
  • 613 TypeScript control-plane tests passed, with one PostgreSQL integration skip behind its environment gate; TypeScript typecheck passed;
  • repository mypy passed for 21 files; Ruff passed all changed Python files;
  • PostgreSQL command validation passed 4 contract tests, with one live-server test skipped behind its environment gate;
  • public/private boundary scan passed; diff hygiene passed;
  • strict receipt cqr_c352b3f10e91b548b0b6 is valid for the exact head and diff;
  • premerge passed 17/18 checks with zero manual holds. The only non-pass was the existing CLI output budget regression smoke hitting the fixed 120-second gate timeout; the same smoke completed successfully standalone. No related assertion failed.

No real Lark message was sent. Merge decision remains hold for reviewer; this PR was not self-merged.

@huangruiteng huangruiteng 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.

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

评审 exact head:d3ea96ef95b0b879f5807501d11364231eea766c。这是对扩展后完整 23 文件 diff 的重新评审;结论仍是 Request changes,不是合并许可。

动机

本 PR 想让用户在 Goal Channel 中对某个 Agent 发出明确的“生成周报”请求,并复用现有 periodic-report 的编辑、冻结产物、投递 Todo、发布和回执链路。新 head 进一步把原先 composition root 对 Lark reader/settler 的直接认识,改成 extension manifest 声明、运行时发现、capability 组合的 provider adapter。方向上,“入口留在 provider、报告状态机留在 capability、kernel 不依赖 Lark”是正确的边界。

但原始用户问题只是一个手动报告入口。目前完整改动达到 23 文件、+2603/-61(生产约 +1352/-60、测试 +1184/-1、文档 +67),并引入新的通用 manifest schema、动态 import/factory、端口组合和多 provider 结算语义。相对原问题,这个机制成本和长期维护面尚未被证明成比例;新抽象还出现了未定义的多 provider 所有权行为,并且原有两个行为 blocker 没有修复。

改动思路

正向路径是:安装并启用且 doctor-ready 的 Lark extension → [[hook_adapters]] 声明 reader/settler ports → discover_extension_hook_adapters 校验权限、激活态、factory 和精确端口集合 → build_quota_interaction_projection_hooks 把 reader 注入已有 pending-intent hook → 原生 mention/reply 被转为内容无关 manual request → quota 投影 governed capability intent → consume-pending 复用既有 editorial、generation bundle、Workspace projection、publication candidate 与 delivery Todo → durable receipt 写入后重新发现 settler 并 ACK provider inbox。

负向路径中,未安装或已禁用 extension 时 discovery 不返回 ports;factory/activation 失败被隔离成 hook failure,base periodic-report hook 保留。生命周期测试覆盖 absent → enabled → disabled,extension runtime 与 import-boundary 测试通过。顶层 CLI、quota 和 scheduler follow-up 已移除 Lark-specific reader 构造,新增模块有真实生产调用点,不是测试专用脚手架。

不过多 provider 组合丢失了 observation 的 adapter/port provenance;settlement 只能按注册顺序盲试 handler。这个状态机无法区分“不是我的请求”和“我是 owner 但暂时失败”,因此并未完成它公开宣称的 provider-neutral 组合契约。

具体改动

  • loopx/extensions/manifest.py、loopx/extensions/hook_adapters.py:新增 hook_adapters schema、权限/phase/token/port 校验、动态 factory 发现以及 failure hook 隔离。
  • loopx/capabilities/interaction_hooks.py:把 extension ports 组合为 periodic-report reader/settler,并为 quota/scheduler 提供统一 hooks。
  • loopx/extensions/lark/extension.toml、provider.py、periodic_report_request.py:Lark v1.6.0 声明 adapter,解析 Agent/Goal Channel/inbox binding,识别请求并提供 read/settle ports。
  • pending_intent.py、post_writeback_hook.py、cli.py:把 manual request 绑定到既有 preset/trigger,复用 durable report 链路并支持 settlement-only retry。
  • event_inbox.py、group_history.py、goal_topic_runtime.py:保留 sender、原生 mention 和 verified reply 所需证据;原始消息不进入 capability observation。
  • quota.py、quota_scheduler_followup.py、顶层 CLI 路径:改为 capability-owned discovery,不再直接 import Lark request adapter。
  • 两份协议/extension 文档说明启用、doctor、权限、失败隔离和升级要求;六个测试文件覆盖 manifest、lifecycle、CLI、intent、Goal Channel 与 import boundary。

行为变更有文档披露;通用 hook/port 命名没有夸大 Agent authority,也没有给 extension 新增隐式写权限。machine obligation 在形成 governed intent 后是明确的,问题不在“guidance”措辞,而在此前的自然语言分类会把非请求错误提升为强制动作。

对主干的风险

[P1] 讨论/教程询问仍会被提升为报告生成请求

位置:loopx/extensions/lark/periodic_report_request.py 的 _REPORT_ACTION / _explicit_weekly_report_request。规则仍是任意位置分别命中“周报/weekly report”和“写/write”等子串,再排除少量黑名单。

在当前 exact head 经真实 ingest → read 独立复现:“我写过周报,想讨论一下格式”和“how to write a weekly report?” 都产生 manual request。下游会把它变成 must_attempt 的 governed capability intent,并进入已有订阅授权的生成/投递链路。最小修复是使用收敛的 typed intent 结果(request/discussion/ambiguous),不确定时不升级;加入这两个反例及中英文明确命令、取消、完成查询的成组测试。

[P1] 多 provider settlement 会被首个非 owner 的失败结果永久截断

位置:loopx/capabilities/interaction_hooks.py 的 _compose_request_settler。它在第一个 handler 返回任意 Mapping 时立即返回,即使结果是 {"ok": false, "status": "request_not_owned"}。因此后面的真实 owner 永远收不到 ACK;durable receipt 每次 retry 都重复同样顺序,provider 请求会永久处于 settlement pending。

独立两-provider probe 以第一个 handler 返回 non-owner failure、第二个返回 settled,当前实现只调用第一个并失败。不能简单把所有 ok=false 都忽略,否则会吞掉真实 owner 的 retryable failure。最小修复是让 reader observation 持久携带受校验的 extension/adapter ownership provenance,并把 settlement 精确路由到同一 binding;或定义 typed not_owned 与 owned_but_failed 结果并测试顺序无关、owner 失败重试和重复 ACK。

[P1] Lark extension 版本升级破坏现有 Miaoda CLI 合同,required CI 已失败

位置:loopx/extensions/lark/extension.toml 将 bundled provider 升到 1.6.0,但 tests/extensions/test_periodic_report_miaoda.py 的 delivery/sink fixture 仍绑定 1.5.0。安装当前 bundled extension 后执行 periodic-report publish-miaoda 返回 active Lark extension does not match the Miaoda sink binding,而不是预期 preview 成功。

远端 required test-shard (1) 因同一测试失败,aggregate pytest 随之失败;本地单测也稳定复现。最小修复是更新并集中复用 canonical bundled Lark version,使已有 Miaoda binding 测试继续证明精确版本契约,而不是放宽生产版本匹配。

[P2] 另一个 Agent 的不同 target 仍会使当前 Agent 的有效请求不可见

位置:periodic_report_request.py 的 _resolved_request_context 调用 binding_for_goal(... provider_target=target, connection_id=...)。底层在按 connection id 过滤前,先用 alpha target 解析所有 sibling connection;加入 beta Agent 的独立 target 后 alpha 读取抛出 Goal Channel provider target does not match target_ref,上层捕获后静默丢掉 observations。

最小修复仍是先精确选择当前 Agent/connection 的 raw binding,再用它自己的 target 解析;加入双 Agent/不同 target 正例和当前 Agent 自身配置漂移的负例。

默认关闭隔离目前只有 discovery lifecycle 的局部配对:absent/disabled 不提供 ports,enabled 提供 ports;尚未把 pre-change、disabled、enabled 在 quota、scheduler、consume-pending、help/schema 与 persisted projection 上做完整 paired counterfactual。新增 framework 的 scope-fit 有真实 caller,但 change proportionality 仍为 not yet proven:对一个手动入口引入 415+ 行通用生产机制,却缺少稳定的多 provider ownership contract;这是独立阻塞条件。

我的整体评价

复用现有 periodic-report 状态机、provider 私有消息不越界、durable receipt 后 ACK、通过 manifest 移除 composition-root 的 Lark import,这些都是正向设计。新增 schema 校验、doctor/permission gate 和 failure isolation 也有可维护价值。

但当前 head 不能合并:两个已发布行为 blocker 原样存在,又新增了多 provider settlement 截断;bundled version bump 还让 required CI 和现有 Miaoda CLI 合同直接失败。建议先做最小行为修复并补齐回归;再决定通用 adapter framework 是否保留。若保留,必须先定义可验证的 observation ownership/settlement 路由,完成 disabled/enabled 全共享面 counterfactual,并说明其相对原问题的比例价值;否则回退到更窄的 provider/capability seam。

当前 exact-head 独立验证:extension runtime + control-plane import boundary 75 passed;四个独立负向 probe 4 failed(两条讨论误判、sibling Agent target、multi-provider settlement);Miaoda preview 单测另有 1 failed,与远端 test-shard (1) 失败一致。远端 DCO、dependency、build、checks、Windows、shard 2 成功,但 required Python aggregate 不通过。未发送真实 Lark 消息,也未执行外部写入。

English verdict: REQUEST_CHANGES at d3ea96e. The manifest-based provider boundary has real callers and 75 focused runtime/import tests pass, but discussion is still promoted to generation, a sibling Agent target still hides valid input, multi-provider settlement stops at the first non-owner failure, and the v1.6.0 bundled-version bump breaks the existing Miaoda preview contract and required CI. Four independent negative probes plus the focused Miaoda test fail. Proportionality and full default-off counterfactual evidence remain blocking; no merge performed.

@huangruiteng
huangruiteng force-pushed the codex/goal-channel-weekly-report-trigger branch from d3ea96e to abc0449 Compare September 6, 2026 10:41
@huangruiteng huangruiteng changed the title feat(periodic-report): trigger reports from agent-addressed Goal Channel requests fix(lark): isolate Agent Goal Channel binding resolution Sep 6, 2026
Signed-off-by: huangruiteng <huangrt01@163.com>

@huangruiteng huangruiteng 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.

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

评审 exact head:abc0449d44766bf96a2220a8618ce84feaa333a9。结论:Request changes;不是合并许可。

动机

这个 PR 把此前较大的 Goal Channel 周报入口改动收窄成一个通用 binding 修复:当同一 Goal 有多个 Agent connection、每个 connection 引用不同 provider target 时,读取指定 connection 不应先拿它的 target 去解析所有 sibling binding。旧行为会让一个无关 sibling 的 target mismatch 抛错,进而隐藏当前 Agent 的有效 binding。问题真实、影响现有 setup/doctor/notification/topic 等生产调用,拆成 2 文件的窄修复是正向的。

改动思路

binding_for_goal 现在先用不带 provider target 的 bindings_for_goal 读取 raw candidates,再按 connection_id、agent_id 或 default connection 选中一个,最后只对该 selected binding 调用 _resolve_goal_binding。正向路径中 alpha target 只解析 alpha connection,beta sibling 不再干扰;负向路径中显式选择 beta 却传 alpha target 仍抛 does not match target_ref,失败归属保持在被选中的 binding 边界。

这个 decision boundary 比“先解析全集、再筛选”正确,也没有增加新 schema、权限或 provider 写动作。它是默认生效的 bug fix,不声称 default-off;authority 名称、guidance/obligation 和领域文案均未变化。

具体改动

  • loopx/extensions/lark/goal_channel_contracts.py:binding_for_goal 改为 select-then-resolve;connection_id 优先于 agent_id,缺省时仍读取 stored default,最后统一返回 _resolve_goal_binding(selected, provider_target=...)。
  • _resolve_goal_binding 和 bindings_for_goal 未改变:前者仍校验 target name/provider/channel/identity 并保留 goal-local pinned message;后者仍负责 v0 单 binding 与 v0 connection-set 的兼容读取。
  • tests/extensions/test_lark_goal_channel_binding_isolation.py:新增双 Agent/双 target fixture,覆盖目标 Agent 成功解析和错误 target 仍 fail-closed 两条路径。

关键代码讲解

  • binding_for_goal:从“resolve every candidate then select”改成“select one raw candidate then resolve”,消除了 sibling connection 对当前选择的非局部影响。
  • _resolve_goal_binding:仍是 provider target 解析和身份/channel 合并的唯一 owner;本 PR 没有复制 target 校验规则。
  • test_binding_resolution_isolates_selected_agent_from_sibling_target:证明 intended positive/negative contract,但尚未覆盖当前 main 新增的 invalid-default deterministic fallback 合同。

精确 diff 为 2 文件、+93/-22:生产 +31/-22,测试 +62,没有机械搬移、文档、生成文件或私密材料。scope fit 有多处真实生产 caller,改动规模与问题成比例;未来向前看的最小重构就是在 rebase 时保留 default fallback 的单一确定性规则,不需要再加一层抽象。

对主干的风险

[P1] 当前 head 与 main 冲突,并会回退刚合入的确定性 fallback 合同

GitHub 当前 mergeStateStatus=DIRTY。main 的 6abf5b289 已把失效 default_connection_id 的读侧 fallback 改成 min(connection_id),与 disconnect writer 的 promotion 规则一致;本 head 在同一函数把 fallback 写回 candidates[0]。独立 probe 用三个按非排序 insertion order 存储的 connection,当前 head 选择 lark_f598...,而 main/writer 合同要求 lark_2b2c...,matches=False。

这不是可忽略的文本冲突:若解决冲突时保留本 PR chunk,notify/setup/automation read 可能在 stale default 下选到与 writer 不同的 Agent topic;若只保留 main chunk,原 sibling-target 隔离修复又会丢失。最小修复是 rebase 当前 main,并合并两条规则:先 raw select;default 缺失时 min(connection_id);只对最终 selected binding resolve provider target。请保留/扩展 main 的 invalid-default fixture,并加一个组合 fixture,证明 non-sorted siblings + invalid default + provider target 同时满足 deterministic fallback 与 target isolation。

本地 exact-head 验证:新 fixture、shared-target 和 Goal Topic connection suites共 39 passed;Ruff 与 git diff --check 通过。远端当前 head 没有 status checks。未执行真实 Lark 写入;测试只读/合成状态足以验证纯 binding selector,但在冲突解决后的新 head 仍需重跑这些 focused suites 与 required CI。

我的整体评价

select-then-resolve 是正确且最小的修复,拆掉此前与 periodic-report 入口混杂的大范围改动也显著改善了可审阅性。我支持保留这 31 行生产改动及 focused fixture;当前唯一实质 blocker 是与最新 main 的同函数冲突会造成跨 Agent 路由回退。rebase 后把 min(connection_id) 与新 selection boundary 组合起来、补一条组合回归并跑绿 CI,即可快速复审。当前不合并。

English verdict: REQUEST_CHANGES at abc0449d44766bf96a2220a8618ce84feaa333a9 (author-owned PR, so published as COMMENTED). The select-then-resolve fix is narrow and correct, and 39 focused tests plus Ruff pass. However, the head is merge-conflicted and restores insertion-order candidates[0], regressing current main commit 6abf5b289 where invalid defaults deterministically fall back to min(connection_id) to match the disconnect writer. Rebase and combine both rules, add the combined regression, then rerun required CI; no merge performed.

@huangruiteng
huangruiteng force-pushed the codex/goal-channel-weekly-report-trigger branch from abc0449 to 3901256 Compare September 6, 2026 10:51

@huangruiteng huangruiteng 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.

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

评审 exact head:3901256d3c472717b36ac677d8a08ed01cce3a34。结论:Approve;由于这是 author-owned PR,GitHub 不允许正式自我批准,因此以 COMMENTED 发布,不是额外的合并授权。

动机

同一 Goal 可以绑定多个 Agent connection,并且每个 connection 可以引用不同的 Lark provider target。旧实现会先把调用方提供的单个 target 应用到所有 sibling binding,再按 connection/Agent 选择;因此一个无关 sibling 的 target mismatch 就能阻断当前 Agent 的有效 binding。这个问题会破坏跨 Agent 隔离,修复目标明确且直接对应生产调用路径。

改动思路

当前实现改为 select-then-resolve:先读取不带 provider target 解析的 raw candidates,再按显式 connection_id、agent_id 或 default 选择唯一 binding,最后只对被选中的 binding 调用 _resolve_goal_binding(..., provider_target=...)。这样 sibling target 不再污染当前选择,同时当前 binding 自身 target 错配仍然 fail closed。

此前复审指出 rebase 冲突会把失效 default 的确定性 fallback 从 min(connection_id) 回退为 insertion-order candidates[0]。本 head 已同时保留 main 的 min(connection_id) 规则和本 PR 的 select-then-resolve 边界,因此读侧 fallback 继续与 disconnect writer 的 promotion 规则一致。

具体改动

  • goal_channel_contracts.py:binding_for_goal 先选 raw connection,后做 provider target 解析;显式 connection 优先于 Agent,缺省读取 stored default,default 失效时按最小 connection id 确定性选择。
  • _resolve_goal_binding 仍是 target name/provider/channel/identity 校验的唯一 owner,没有复制校验逻辑,也没有放宽错误 target。
  • 新增隔离测试覆盖 alpha connection 不受 beta target 干扰,以及为 beta 错传 alpha target 时继续拒绝。
  • 保留既有 invalid-default 与 disconnect promotion 回归。独立组合 probe 进一步覆盖 non-sorted siblings + invalid default + 每个 connection 不同 target:fallback 选择最小 connection id、只用其 target 解析,错误 target 仍被拒绝。

精确 diff 为 2 文件、+98/-26,属于 Lark extension 内的窄 bug fix,没有新增 schema、provider 写动作、权限或抽象层。

对主干的风险

风险较低且边界清晰。默认生效是因为它修复现有 binding selector,而不是新增 activation surface;typed state、authority、default-off activation、guidance/obligation 语义均未变化。主要风险是 selector 优先级或 stale-default 行为回退,但当前实现和测试同时守住了显式 connection/Agent 选择、确定性 fallback、selected-target 校验三条合同。

独立验证结果:focused suites 42 passed;Ruff 与 git diff --check 通过;组合语义 probe 通过。远端 12 项 checks 全部完成且成功(发布任务按预期 skipped),GitHub 当前报告 MERGEABLE,BLOCKED 仅来自 review requirement。未执行真实 Lark 写入。

我的整体评价

这是正向且成比例的修复。它把解析边界放在正确位置,消除了 sibling connection 的非局部干扰,同时保留 main 已建立的确定性 fallback,不需要顺带引入新抽象。我批准当前 exact head;heartbeat 本身不执行合并。

English verdict: APPROVE at 3901256d3c472717b36ac677d8a08ed01cce3a34 (published as COMMENTED because GitHub blocks formal self-approval on an author-owned PR). The select-then-resolve boundary correctly isolates sibling Agent targets, preserves deterministic min(connection_id) fallback, and keeps wrong-target resolution fail-closed. Forty-two focused tests, Ruff, diff-check, an independent combined semantic probe, and all remote required checks pass. No merge performed.

@huangruiteng
huangruiteng merged commit 255295f into main Sep 6, 2026
12 checks passed
@huangruiteng
huangruiteng deleted the codex/goal-channel-weekly-report-trigger branch September 6, 2026 12:07
yanfeng98 added a commit to yanfeng98/nano-loopx that referenced this pull request Sep 6, 2026
- 255295f lark agent goal channel 绑定隔离 (loopx-project#3992)
- 64a0ab6 长程定位精修 + 1.0 工作区指南 (loopx-project#4008)
- 89ac4d5 workspace Goal 目录优先渲染 (loopx-project#4007)

文档融合: README.md 采用上游官方中文版(678 行)升为主文件,
修复其中 English/zh-CN 链接与双语表述;readme-demo-surface-smoke
断言按新官方中文内容适配(语义等值)。

Co-Authored-By: Claude Code <noreply@anthropic.com>
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.

1 participant