Conversation
Signed-off-by: Jim-jimu <49069997+bmh201708@users.noreply.github.com>
huangruiteng
left a comment
There was a problem hiding this comment.
结论:APPROVE。四行既有 TypeScript owner 校验,修复了“admit 产出的重复 source refs 到 retire 才失败”的真实续接缺陷;合法输入和既有拒绝/未激活分支的完整输出保持一致。
English verdict: APPROVE
Reviewed exact head: 7553635
动机
外部证据被父 Agent 明确采纳后,admission 必须能被后续 projection/retirement 正常读取。原 producer 允许选择同一个 source ref 多次,但既有 admission reader 又要求 refs 唯一,于是当前命令成功产出的回执,在下一步被自己拒绝。这里要修的是一个可复现的闭环缺陷,不是用更多 receipt 或更大的框架证明外部研究能力整体完成。
改动思路
把已有的唯一性要求前移到 evaluateExternalEvidenceAdmission:先执行现有 boundedStrings 的 trim/数量/长度校验,再用 Set cardinality 明确拒绝重复;之后继续走原 membership、disposition、projection 和 digest 构造。Python CLI 仍然只负责参数与结果运输,共享决策仍在 TypeScript owner。
我也比较了更短的替代方案:自动去重会悄悄改写父 Agent 的显式采纳意图;删掉 retirement reader 的防御则会接收外部/历史畸形 admission。这两种都不是等价修复。producer 和 reader 分属不同信任边界,保留两处检查是合理的,不需要为了四行规则抽出新框架。
具体改动
实际范围是三个文件、+134/-0:生产 TS 函数只增加四行;49 行 owner 回归和 81 行 CLI 回归验证新输入边界及 admit→retire。没有新模块、协议版本、CLI 参数、持久化字段或新的 Python 决策源。
关键代码讲解
- evaluateExternalEvidenceAdmission:admitted_source_refs 经原有 normalization 后执行 new Set(admittedRefs).size === admittedRefs.length。直接重复、首尾空白归一化后的重复、非相邻重复都会在 admission 构造前失败;不同来源的选择和原有顺序没有被改写。错误具体指向 decision.admitted_source_refs。
- normalizeAdmission:既有 reader 对输入 envelope/schema/digest/disposition/projection 和 refs 唯一性继续做独立校验。它必须处理随后由 caller 提供的历史或不可信数据;producer 新校验不能替它背书,所以保留不是第二个状态权威。
- projectExternalEvidenceRetirement:仍按已采纳来源是否全部投影判断 retained/retire_ready,部分覆盖或无关来源不能提前退休。现在合法新回执可以继续,非法重复选择则不会先产生一份毒回执再失败。retire_ready 也不是 Goal 完成或外部执行权限。
我本人用同一个独立合成 fixture 跑 production CLI→实际隔离 managed TS runtime,覆盖 14 个场景:单来源、子集、有序多来源、64 个唯一来源;直接/空白/非相邻重复;拒绝后的修正输入;未知来源、空 admit、空 reject、reject 带来源;部分/无关覆盖;声明但禁用的 provider。base 的三个重复场景先产出 admission,再在 retire 因唯一性失败;head 则 exit 1/invalid_request 且无 admission_id,改为唯一列表后完整到 retire_ready。其余 11 个场景的完整 normalized observation 在 base/head 逐字一致,包括错误细节、source order、id 和 coverage readback,不只比较通过数量。
对主干的风险
主要反向风险是过度拒绝合法多来源、静默重排/去重,或让“来源存在/安装”被误当成采纳/激活。上述独立真实 CLI 路径已经覆盖这些反例:不同来源和 64 边界通过,部分覆盖仍 retained,禁用 provider 仍未选中;没有新增自动调用、调度或外部副作用。语义变化只有重复选择在 producer 更早失败,这是修正原有不一致,已在 PR 与命名回归中披露。
本地验证:TS owner 16/16、CLI 8/8;TypeScript typecheck、配置范围 ruff/mypy、实际 PR diff check 全部通过;原生 TS 全量 3415 passed、30 既有条件 skip、0 fail;risk-selected premerge canary 13 项、0 fail;exact-scope change-quality 已记录 pass 并 verify。30 个条件 skip 没有冒充 PostgreSQL 实测,本 PR 不改 authority store/PG adapter。本次不查询、不轮询、不等待 GitHub CI。
语义与 CI 对齐
沿用了既有 disposition/schema 和唯一来源契约,匹配依据是明确归一化后的来源身份,不是 prose/substring denylist。错误文案保持领域中立;这是 enforced input validity,不是含糊的 guidance,也不增加 must_attempt_work。无 actor 生命周期/权限扩张,无自动加载 instruction 改动。证据与 provider 内容为合成输入,不宣称执行了真实外部服务、验证了来源内容质量或完成了 frontend/Lark 集成。
我的整体评价
这是完整、可逆、适当大小的旧 owner 修复:纠正了实际 producer/reader 不一致,也验证了随后能继续工作的路径。future-facing pass 的结论是无需额外抽象,保留独立 reader 防御即可。更大的 issue #5205 method/connector/frontend/Lark 集成仍是另一项边界,没有被这条 approve 当作完成。此次批准只评价这个 exact head;不代表自动 merge readiness,也没有执行合并。
Goal And Delivered Outcome
--admit-sourcereturned a successful admission containing duplicate references, butretirerejected that same record as invalid even after downstream coverage was complete.main.Scope And Continuation
Validation
7553635b870beea0fc4ae69089fd0ce33207fb26for the final focused rerun. The broader checks below ran before commit, on the same three-file patch over the explicitly named upstream baselines.regression_paritypassedda5aac12with the added tests but unchanged product code: TypeScript 2 failed / 14 passed; Python CLI 2 failed / 6 passed. Exact and whitespace-normalized duplicates were incorrectly accepted. Final commit rerun: 16 TypeScript and 8 Python tests passed.real_entrypointpasseduv run --extra test python -m pytest tests/capabilities/test_external_evidence_cli.py -q: 8 passed on the final commit. Tests usepython -m loopx.entrypointand the managed TypeScript runtime with isolated temporary state. Duplicate input returnsinvalid_requestwithout an admission id; corrected input reachesretire_ready. Receipts are synthetic; no external research provider is invoked.integrationpassedcf759d32, before commit: 29 TypeScript tests across external evidence, manager return delivery and Todo authoring scope; 206 Python tests across the external-evidence CLI, canonical Todo health, collaboration Goal identity and manager-context roundtrips.integrationpassed0730b6cc, before commit: full TypeScript suite 3,412 passed / 30 PostgreSQL cases skipped; 1,202 Python architecture, session, public-safety and CLI tests passed. These were followed by the affected checks oncf759d32above.integrationfailedda5aac12plus this patch: 13,075 passed, 62 skipped, 4 failed. All four failures reproduced on the clean baseline: two registry I/O inventory assertions, and two scheduler-ack assertions intest_quota_settlement_cli.py. Upstream0730b6ccfixed the inventory assertions; the architecture rerun passed. The two quota failures also reproduce on clean final basecf759d32.staticpassedcf759d32plus this patch: repository Ruff check, TypeScript typecheck, mypy (19 source files), CLI output-budget smoke and diff hygiene passed. The public-boundary scan of the three changed files reports zero errors or warnings.integrationpassedloopx canary premerge --from-git-diff --git-diff-base cf759d323b755e2040200f976eac1eeb89383b4d: 13 selected catalog/risk checks passed, with no manual holds.staticfailedcf759d32reports two existing synthetic path fixtures intests/control_plane/test_public_safe_text_classifier.py(lines 100 and 159). The clean final base gives the same two findings; neither file nor fixture is changed here.Frontend / Visual Evidence
Type of Change
LoopX Area
Technical Direction
Shared-authority RFC fixture impact
Boundary Checklist
none.Signed-off-bytrailer (git commit -s).