Repository navigation
fix(collaboration): reject ambiguous original Turn provenance - #8210
loopx-agent wants to merge 4 commits into
Conversation
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: actor_kind=model_agent; model=gpt-6.1-sol; provider=OpenAI; declaration_source=runtime_reported; reasoning_effort=max.
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
动机 / Motivation
在管家入口查看待跟进委派的用户,会在原始来源损坏或重复时得到误导的完整计数。 同一组61条合成交办,旧读取器把一个重复来源也算作已核验;新读取器显示60+与1条未核验,恢复原始来源后重新显示61。 这会让用户误以为交办已完整核验,并把后续跟进建立在未证明的来源上。其他健康请求仍可展开,坏来源不泄露正文;同一原始来源恢复后重新出现,读取不修改来源或协调记录。 本PR不修复历史回执、不判定取消、不新增执行权限,也不宣称首次读取提速或完整委派流程完成。 历史回执冲突的原始发送证据、慢读归因、跨宿主权限、安装态行为和请求方实际采用仍待原流程验证。
改动思路 / Approach
读取入口仍是已有的 GET /api/chat/request-inventory。实际 Chat 宿主提供 Session 和保留的 Turn 文件,Python 只定位并观察物理证据;来源权限、Goal 生命周期与回执状态仍由共享 typed owner 决定。正常来源通过后进入原有列表,失败来源进入已有的未核验计数。每次刷新重新建立路径索引,恢复原始文件后可以前进;读取本身不会修历史记录。
来源唯一性属于现有物理证据读取适配器;权限和生命周期继续由共享TypeScript owner决定,新增第二套状态或迁移整条链都不能改善这次局部不变量。 当前PR只修复原始Turn证据定位、沿用已有不完整覆盖投影并补全故障恢复验证;额外一行夹具清理解除必需Ruff失败。 observe_source_chat 是另一条单来源转发观察路径,不能直接代替这里的批量定位与逐请求授权检查。目录排序后继续选一个来源仍然无法证明唯一性;新增内容缓存、第二个状态 owner 或整条语言重写也没有必要。
具体改动 / Changes
复核范围为 9d276f6cb903ac1bbd465bf60ad4f04b295c5c43 到 71b247644c60768afaa46c1fef29a1b299bc9d5b 的完整五路径差异,85 行增加、4 行删除。其中生产适配器为10+/3−,其余是现有测试与合成夹具75+/1−,没有新增模块、协议、设置或公开 CLI。相关主干 #8220 已在冻结验证之前集成,未继承原提案的通过结论;远端评论、inline comments 和 reviews 当前为空。
关键代码讲解
original_turn_reader在一次读取里保留路径索引。旧逻辑第一次遇到client_turn_id就决定来源;现在重复 key 留为None,包括第三个重复也不会重新变成可用,因此不依赖目录顺序。original_turn_reader.read每次使用时重新读文件,必须同时匹配既有 schema、Session、文件名 Turn 身份和 client id,才返回正文。它没有缓存内容、恢复 Chat store 或授予权限;拒绝后由现有逐请求异常分支增加未核验数,健康 key 继续读取。test_unproved_original_turn_is_withheld_without_hiding_other_requests在真实 HTTP 与 File/SQLite 的一次性存储内注入四种来源错误,检查 unfinished/history/search 不泄露坏记录、健康邻居仍在、读取前后字节不变,随后恢复来源并核对两条请求都重现。lose_response只去掉未使用的赋值。原方法仍先执行,再抛出模拟丢失响应的TimeoutError;完整模块继续检查幂等恢复和一次派发,没有把必需 Ruff 失败降级。
规范引用:loopx/capabilities/manager_context/README.md,固定版本 9d276f6cb903ac1bbd465bf60ad4f04b295c5c43(同一文本)。以下 ID 是原文行锚点:manager-context:L267-L275 的不可读来源保持部分覆盖、manager-context:L278-L285 的逐请求来源/权限/回执核验、manager-context:L287-L296 的刷新原 owner 且不修回执、manager-context:L323-L341 的完整原 Turn 阅读,以及 manager-context:L343-L350 的只读 agenda,均由既有 owner 保持并在本次真实路径验证;未把未来安装态或采用效果提升成当前已交付事实。
| 同输入反证 | 当前主干 | 当前 PR |
|---|---|---|
| 重复 client、外来 Session、外来 Turn、未知 schema × File/SQLite | 8 个断言失败:错误来源仍被计入完整覆盖 | 8 个故障用例通过,邻居和恢复保留 |
| 正常内容重读、已读/采用后仍欠结论 | 4 个控制通过 | 同样4个通过 |
另补同输入的两种 provider 新来源用例:先读取1条,再新建并交办合法来源,刷新得到2条;主干与PR各2通过。该本地 harness 最初缺少 pytest fixture 注册的 setup 失败保留,补齐注册后执行,不涉及生产代码或断言变更。
这12个用例在各自 checkout 和独立环境执行相同测试源码,经过实际 HTTP handler;主干8失败4通过,PR12通过。完整四模块另外222通过(359.85秒);打包 smoke:delegation-cancel 在61条 File/SQLite 中覆盖分页、取消、晚到搜索、来源故障/恢复、15,620字符原文和返回答案。两者 inventory 写入和新模型 Turn 均为0,但保留4/5次普通 reader Session resume,不能说没有HTTP POST。
对主干的风险 / Risks
最强反例是:更严格的来源检查虽然挡住重复记录,却把同批健康请求或恢复后的记录一起隐藏。这次通过实际相邻请求身份、部分计数、后来有效 Turn、恢复后的完整集合与字节保全断言排除了该反例。生产调用在 conversation_inventory.py:190-200,并非仅测试可达。破损来源需要原 owner 恢复;UI 的通用未核验提示没有诊断具体损坏原因,这一局限保留。
必需 Ruff、配置内17文件严格 mypy、control-plane TS、Dashboard/verified Chat 构建、精确五路径 CQR cqr_556d65378f05ac69c9c8 和 premerge 的6项直接+13项选中检查均通过。可选直接文件 mypy 仍失败:当前主干/PR各4,576 errors /463 files,完整4,731行按顺序一致,只有同一个未改动函数的行号76→83;命令、配置/fixture和环境摘要匹配,无 import 改动。该失败按 pre_existing_unrelated 保留,不是全仓 typecheck通过或必需检查豁免。旧4,557错误截断记录、旧head的Ruff和advisory缺文件CI失败仍是历史失败;当前配置不等待CI,本轮没有查询新headCI。
这是现有能力始终生效的证据完整性修复,没有 opt-in/default-off 声明;有效来源的旧行为用上述控制对照保留,坏来源的默认行为收紧已公开。没有新 actor、权限、生命周期或公共语义词表;局部 Path | None 是物理证据推导值,不是手工同步的任务状态。通用规则没有引入领域词或把建议伪装成硬性工作义务。回退可独立撤销 reader hunk 与测试夹具清理;实际恢复路径是恢复同一原始来源再刷新。完整 pytest、真实PostgreSQL、live Lark/model、安装态Mac/WebView和物理IME未验证。
我的整体评价 / Verdict
交付判断是 justified_increment:一个既有读取 owner 内的来源正确性修复,机制成本与误导完整计数的风险相称。long_horizon=preserved,没有测出或宣称长期净收益和完整成本改善;user_experience=improved,坏来源的缺口能被看见且健康请求仍可用。既有 v1格式与当前调用方兼容,不增加兼容 wrapper;保留小索引并删除无用测试赋值,整链迁移留在有实际需求的边界。
视觉复核原因:已有部分覆盖投影的可见计数发生变化,不能只看安静列表。当前打包页面的61条内容、混合状态、展开正文、1440桌面及390窄屏已实际检查,公开截图在PR正文可加载,窄屏无横向溢出。未验证范围:安装态Mac/WebView、物理IME、live Lark、私有历史来源恢复和请求方实际采用。精确当前head未发现阻塞性问题;作者结论不等于独立review或maintainer merge授权,后续更改head需重审。历史回执冲突的原始发送证据、慢读归因、跨宿主权限、安装态行为和请求方实际采用仍待原流程验证。
English verdict: APPROVE this exact head for the bounded provenance repair; optional broad mypy remains failed, independent review and maintainer merge authorization remain separate.
Problem And Result
委派列表曾在两个历史 Turn 共用
client_turn_id时,按目录顺序选择一个来源并报告完整覆盖;错误的 Session、Turn 或 schema 身份也能通过读取。现在无法唯一证明的来源会保留为读取缺口,健康请求仍可查询;恢复同一个原始来源后,请求重新出现。The same 61-request fixture changes from a falsely complete 61 to 60+ with one unverified record, then returns to 61 after exact source recovery. This proposal fixes that bounded provenance defect; it does not repair historical records or declare the wider delegation workflow complete.
Implementation And Ownership
original_turn_readerretains the existing per-read path index, marks duplicate client identities unavailable, and rereads the existing schema, Session, Turn filename and client identity before returning a Turn.TimeoutError; recovery and single-dispatch assertions remain intact.manager-contextbuiltin /loopx-core. Python remains the physical Chat-filesystem provenance adapter. Shared TypeScript permission, lifecycle, agenda and return authority remain the owners. No new schema, setting, cache, module or execution permission.The bounded refactoring pass retains the small reader/index and removes the unused fixture binding. A language migration or additional wrapper would enlarge this repair without improving its owning contract.
Author And Specification
Written by a model agent, Codex / OpenAI. The existing original-source, read-only inventory and original-conversation contracts in
manager-context, together with the reproduced defect above, define this repair.original_turn_readerValidation
Tested clean head:
71b247644c60768afaa46c1fef29a1b299bc9d5b, integrated target main9d276f6cb903ac1bbd465bf60ad4f04b295c5c43. Five-path proposal; all contribution and integration commits carry DCO sign-off. Run state: finished. Input classes: synthetic, public fixture. No live model, installation, release or merge.test_chat_request_inventory.py,test_native_steward_ingress.py,test_manager_context_roundtrip.py,test_goal_create_completion.py; disposable File/SQLite and native-host fixturesnpm run smoke:delegation-cancel --prefix apps/presentation/dashboard, both 61-request backends; pagination, cancellation, late search, source fault/recovery, complete 15,620-character original body and returned-answer readingcqr_556d65378f05ac69c9c8, exact five-path fingerprint556d65378f05ac69c9c8834dc4196cb69c63c959bdcad4f7997704106141a732; one bounded safe-fix pass; the optional typecheck failure below remains recordedRetained failures and matched attribution
The earlier head
e1d29c2retained a required Ruff F841 failure and a truncated exploratory mypy run reporting 4,557 errors. That failed receipt and those observations remain historical failures. The unused fixture assignment is now repaired; required Ruff passes.The optional command
uv run --extra test python -m mypy loopx/control_plane/collaboration/source_chat_observation.pystill fails. Fresh independent runs at the immutable current base and head each report 4,576 errors in 463 files. Their complete 4,731-line outputs agree in order after location-only normalization; command, configuration/fixture and environment digests match. The only raw difference is the same unchanged untypedtrusted_source_chat_rootfunction moving from line 76 to 83. No import edge changed. This is exact-scopepre_existing_unrelatedattribution for an optional check, supported by the passed required invariants over all five changed paths. It is not a full-tree typecheck pass or a waiver of required validation.Historical CI on
e1d29c2also showed the same Ruff failure and an advisory Ark job referencing a missing test file. New-head CI is unobserved under the configured no-CI-wait policy; none of the old failed/skipped jobs is relabeled green. Exact-head review, independent review and maintainer merge authorization remain separate.Frontend Evidence
Public synthetic billing-export requests over the real disposable backend. The UI layout is unchanged. The primary question remains which delegation owes a result and whether its sources can be verified. Grouped rows, explicit loaded/total counts and existing one-click uncertainty help remain central; complete briefs, source messages and provenance remain available on demand. The partial-coverage help is generic; it does not identify the exact corruption cause.
Current-head desktop, 1440×1000: healthy rows remain readable and one unverified source is disclosed.
Current-head narrow expanded brief, 390×844: readable constraints and acceptance, retained partial count and pagination.
Current-head exact source recovery, 390×844: 61 records and the uncertainty marker removed.
Original matched before/after evidence, retained with its revision boundary
The original baseline reader and the same built frontend falsely count an ambiguous source as complete:
Original proposal
e1d29c2, desktop/narrow partial coverage and recovery. The production reader hunk is unchanged in the current head; the current-head journey above revalidates it with integrated main.Remaining Scope And Rollback
Existing historical receipt conflicts still require original sending/correction evidence; this patch neither rewrites them nor infers cancellation. Slow-read attribution, full multi-host permissions, installed Mac/WebView/Lark behavior and requester adoption remain with the existing delegation work. This PR makes no latency, complete-cost or long-horizon benefit claim.
Exact source restoration is the runtime recovery path. Reverting the proposal removes this bounded reader protection and its regressions; the test-only unused-binding cleanup can be reverted separately. No merge, installation, release, provider execution or new protected-operation authority is part of this proposal.