Repository navigation
refactor(collaboration): converge Inbox reads and Turn context in TypeScript - #6860
Conversation
…eScript Signed-off-by: aihop <4408344+hhyykk@users.noreply.github.com>
088d2f6 to
987bd10
Compare
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: model_agent · gpt-6.1-sol · OpenAI · runtime_reported · reasoning_effort=max
Exact head: 987bd101b7bd34280d67e50bcbec1b974f23e6c8 · immutable baseline: 2d94432eefc1d9ee2438e8b6a7cf98b0ebf26971
动机
通过 CLI、MCP 或 Turn-start hook 读取既有协作请求的 Agent 与维护者。
同一批 128 条请求仍应返回当前 20 条、保留全部未完结计数,并只登记实际读到的请求;目录损坏应返回可解析的失败,而非空输出。
旧实现由 Python 扫描本地请求、组合多次跨语言调用;本次把读取与原有读回执登记收敛到同一个 TypeScript 事务。正常本地 socket 路径已验证跨语言调用减少;损坏目录的 CLI 错误契约出现回归。
结论:REQUEST_CHANGES。现有正常 CLI 负例在基线通过、当前版本稳定失败;此外默认 SQLite 的完整性能资格仍未完成。
本 PR 不新增项目助手接收者、执行驱动、队列、发送权限或原对话回传,也不代表 2.0 原地升级已经完成。
改动思路
事务所有权放置合理,且正常读取有可验证收益;目录 IO 异常未适配到原 CLI 错误边界,默认 SQLite 完整性能资格也仍未提供。
当前 PR 应仅交付现有 Inbox 读取、完整计数和原格式读回执的收敛;项目型接收者、完整回传和安装推广保持独立边界。
复用 inbox_receipts、goal_instance_lifecycle、request_control、result_publication 与 turn_context 是合适的:规则和持久读回执交给原有 TS owner,Python 保留真实 Core、工作目录与已确认操作的 IO。单独修一个扫描函数不能退役旧编排;也没有理由为这次收敛新增队列、框架或另一套请求数据库。
本轮用同一脚本、相同的 128 条合法来源,执行真实本地 socket runtime。两个临时来源具有相同夹具指纹;保留所有请求、正文、简报、建议和计数,仅剔除观察时间、含临时路径的读命令,并单独比较临时根所绑定 cursor 的原请求锚点:
| 场景 | 基线 → 当前 | 保持的结果 |
|---|---|---|
| Turn hook,有/无简报 | 4 → 1 次运行时请求 | 完整 128 条计数、当前 20 条;不写读回执 |
| receiver,无简报 | 5 → 1 次 | 当前 20 条、20 份原请求读回执 |
| receiver,有空输入简报 | 5 → 2 次 | 保留 Host 输入观察和提交阶段、完整简报及原请求 |
这是本轮独立的 legacy 存储夹具,不是作者 source-session 的 153→1/174→1 数字复核,也不等于默认 SQLite p95 或内存资格。测试 spy 只记录真实请求后转发原调用,没有替代 runtime 的决定或文件效果。
具体改动
完整 diff 为 18 文件、+1078/-647。生产代码及共享 JSON 为 +652/-640,净增加 12 行;测试 +375/-4,文档 +50/-2,包数据配置 +1/-1。新原生 owner 466 行,替代原 inbox.py、peers.py 和 Turn-context 中的扫描、分页、回执组合;五个细粒度 handler 同时删除。33 行 Python 适配器及真实 Host providers 有现存 CLI/MCP/hook 调用者,保留它们具有明确兼容理由。
关键代码讲解
readCollaborationInbox选择原有 pending、hook、receiver、return/read 事务;有真实 provider 工作时延后读回执提交。读取不会自动变成采纳、结果消费或执行。pendingPage保留精确请求读取、20 条展示、lookahead 与原 scope cursor;当前页完整后,后页坏记录不会阻断它。recordReads重新读取原请求,在既有跨 runtime 请求锁内检查修订并等待原子写回;中断后原回执仍可恢复。read_inbox_transaction选择现有大快照传输并转交来源事实。这里仅把EffectRuntimeRejected转为ValueError,没有保留新的远端文件 IO 异常到原调用者的适配,是本轮阻塞点。
规范:docs/architecture/rfcs/typescript-control-plane-migration-v0.md,spec_revision=2d94432eefc1d9ee2438e8b6a7cf98b0ebf26971。以下按 PR 修改之前的 accepted 文本判断:
| criterion_id | 当前处置 |
|---|---|
| 3.2 Payoff-phase decision | implemented:现有领域 owner 组成完整读取事务;旧 Python 编排及五个叶子 RPC 删除;真实 Host IO 保留 |
| 5 Payoff-phase PR contract | implemented:独立核对代码分类、存储/调用者与正常请求计数;现有 Python facade 随其 CLI/MCP/Host 消费者退役 |
| 6 Correctness | not_met:正常读取、分页与回执恢复通过;目录损坏的 CLI 错误边界回归 |
| 6 Caller-observable semantic parity is a promotion gate | not_met:同一个未修改的真实 CLI 测试,基线可解析失败,当前 stdout 为空 |
| 6 Performance | not_met:默认 SQLite 完整 CLI/事务、冷暖 p50/p95 和 daemon 内存仍无合格 matched 证据;请求数减少不能替代 |
对主干的风险
P2:保留文件读取失败的结构化 CLI 契约
位置:inbox_read.py:27–30,关联 names。当已存在的 entries/<target-hash> 目录被损坏为普通文件时,readdirSync 产生 ENOTDIR。移动到 TS 后,错误以 EffectRuntimeIOError 家族返回;新适配器只捕获 EffectRuntimeRejected,而原 CLI 和 hook 调用者仍接收 OSError/ValueError。结果是 CLI 退出码仍为 1,却失去原来的 ok=false JSON,消费者得到空正文和异常栈,无法按原契约进行恢复。
本轮运行未修改的 tests/test_inbox_pagination.py::test_unreadable_or_conflicting_entries_fail_without_read_receipts[directory]:基线 1 passed;当前 head 在完整测试组和单独复验均失败,失败点为 json.loads(result.stdout) 的 JSONDecodeError。 测试文件两个版本字节完全一致,SHA-256 c2e4282a3cabe39add2e698b6ca277cf581e2d1bf51747b5f9d16a545c9852ef。没有以 stdio 替代 socket,也没有把原生 owner 的单元测试当作 CLI 证明。
最小修复在现有 IO 边界完成:把相关 typed runtime 文件异常适配回原调用者契约,或一致地更新全部实际调用者;保留可重试/永久失败信息和原回执。不要捕获所有异常后返回空 Inbox,不要重放已提交副作用。原有测试已经足够捕获这条回归,另补一个普通文件读取失败可验证同一异常家族。
已执行验证与资格边界
- 327 passed、1 failed:十个现有生产入口测试族,覆盖分页、损坏回执、Turn agenda、Goal instance、来源 handoff、tracking、原生进程恢复、精确请求、分离 Chat/coordination roots,以及 bound-owner HTTP。Python 3.14.8、Node 24.21.0;源码为本 exact head。
- 12 个针对修订/取消/MCP/原会话控制的补充用例通过:覆盖真实 receiver 读取、当前修订的返回关系和取消后的 agenda。
- 35 个原生用例通过:新 Inbox 事务、既有 Inbox receipts、Goal-instance lifecycle。它们没有覆盖 Python CLI 异常接缝,不能抵消上述失败。
- 独立同源对照通过:128 请求的有/无简报两种情形,保留原 source 字节、20 条页、128 条完整计数、相同原请求及读回执;脚本 SHA-256
b72061577d29e2275388bd47cf3e2b3d4f0501c6921167cd4d7e9089e2a3048d。 - TypeScript typecheck、变更 Python 的 Ruff、RFC 双语检查和 diff 检查通过。没有查询、等待或轮询 CI。
- 本机实际执行了正常 socket、CLI 子进程、MCP 与 HTTP 路径,原作者在其环境遇到的 EPERM 没有阻止本轮运行。这里仍是隔离 File/SQLite/合成 provider 和模型边界;没有使用真实会话或发送外部消息。
- 新 JSON 包资源声明已检查;作者的 wheel/sdist 检查没有在本轮独立重跑。默认 SQLite 完整性能资格也仍未完成。应沿现有同负载合同补齐,不要求以 File-only 性能或无关 RFC 完成替代/扩大本次门槛。
语义与 CI 对齐
本 PR 扩展的是既有 LOCAL_SNAPSHOT_METHODS,Python/TS 都增加同一个 collaboration.inbox.read;不新增发送或执行权限。diff advisory 报告一个受支持的 vocabulary carrier,剩余操作字面量、JSON codec 与错误行为已人工核对。当前违反的是既有完整诊断与失败恢复契约,最小修复是适配异常接缝,而非扩张业务 vocabulary。
复验:python -m pytest -q 'tests/test_inbox_pagination.py::test_unreadable_or_conflicting_entries_fail_without_read_receipts[directory]',再执行相关 Inbox/CLI/MCP/HTTP 测试和现有默认 SQLite 性能资格。标准预算、原失败和未测边界应保留。
我的整体评价
problem_context=not_yet_proven;范围与机制本身是 proportionate,保留现有领域 owner 和 co-deployed facade 的选择合理。正常读取有可验证收益,不能因为有缺陷就要求恢复旧 Python 规则或重建框架。但当前 whole-PR 的 user_experience=regression:普通损坏来源已不能得到原结构化失败;long_horizon=not_yet_proven,错误恢复及完整默认成本资格仍需补齐。
保留现有 source/GoalRef/请求修订、页顺序、只登记实际读到的记录及重启恢复语义,修复一个 IO 适配边界即可重新判断。当前未发现新的权限扩大,也没有把读回执当作采纳或完成。项目接收者、原会话完整回传、已安装采用和 2.0 原地升级继续由各自原验收负责;本评审不为它们或合并提供额外授权。
English verdict: REQUEST_CHANGES at 987bd10. The unchanged real CLI directory-corruption test passes on immutable base and fails at this head because native filesystem errors bypass the existing structured-error adapter. Independent normal-runtime tests returned 327 passed/1 failed plus 35 native passes; paired 128-request cases confirm fewer crossings and retained source/page/read semantics. Repair the bounded IO seam and complete default-SQLite performance qualification; do not replace unreadable state with an empty inbox.
| snapshot = snapshot or len(request.get("rows", [])) > 1 | ||
| try: | ||
| result = effect_runtime_result("collaboration.inbox.read", facts, large_local_snapshot=snapshot) | ||
| except EffectRuntimeRejected as exc: |
There was a problem hiding this comment.
[P2] 保留跨 runtime 文件失败的结构化读回
当 addressed entries 目录损坏为普通文件时,TS 的 ENOTDIR 以 EffectRuntimeIOError 家族返回;这里只捕获 EffectRuntimeRejected,而现有 CLI/hook 仍捕获 OSError/ValueError,导致 CLI exit=1 却没有原 ok=false JSON。未修改的 directory 回归在当前基线通过,在本 head 的完整组与独立复验都因空 stdout 的 JSONDecodeError 失败。请在现有 IO 适配层保留原错误/恢复契约,或一致地适配实际调用者;不要把损坏来源改成空 Inbox。原 tests/test_inbox_pagination.py::test_unreadable_or_conflicting_entries_fail_without_read_receipts[directory] 已可复现。
…0261019 Signed-off-by: aihop <4408344+hhyykk@users.noreply.github.com>
Moving the Inbox read into the TypeScript transaction changed how a corrupted entries directory surfaces: the native owner reports ENOTDIR as a typed EffectRuntimeIOError, but the Python adapter only mapped EffectRuntimeRejected. The CLI therefore exited 1 with an empty stdout instead of the original `ok=false` JSON, and the existing pagination regression (test_unreadable_or_conflicting_entries_fail_without_read_receipts[directory]) failed with JSONDecodeError. Map EffectRuntimeIOError back to OSError at the same IO boundary, keeping the runtime diagnostic code (and therefore the retryable/permanent distinction) in the message. Callers and the CLI classify IO failures as OSError, so the structured failure and the untouched read receipts are preserved. 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: model_agent; provider=OpenAI; runtime_reported
动机
受影响的是通过 CLI、MCP 或 Turn-start hook 读取既有协作请求的 Agent 与维护者。上一轮的 P2 是:把读取收敛到 TypeScript 事务后,entries/<target-hash> 被损坏成普通文件时 readdirSync 抛出的 ENOTDIR 变成了 typed EffectRuntimeIOError,而 Python 适配器只捕获 EffectRuntimeRejected,于是 CLI 虽然仍以 1 退出,却不再输出原来的 ok=false JSON——现有的 test_unreadable_or_conflicting_entries_fail_without_read_receipts[directory] 因此在 head 上以 JSONDecodeError 失败。
改动思路
修复留在同一个 IO 边界:适配器把 typed runtime 的文件异常映射回调用方原有的 OSError 契约,并把 runtime 的诊断码(因此也保留 transient/permanent 语义)带进消息。没有捕获全部异常后返回空 Inbox,也没有重放任何已提交的副作用;正常读取、分页、完整计数与读回执逻辑均未改动。本头同时基于贡献者最新的 main 合并提交,重新提交了这条修复。
具体改动
相对 base 的净新增为 loopx/control_plane/collaboration/inbox_read.py 的 +10/−1:导入 EffectRuntimeIOError,在 read_inbox_transaction 的 except EffectRuntimeRejected → ValueError 之后新增 except EffectRuntimeIOError → OSError(f"{diagnostic_code}: {message}")。其余文件保持贡献者原样。
对主干的风险
只有错误映射变化,不改选择、存储、权限或读取语义;异常仍带诊断码,恢复路径可以区分暂时与永久失败。实测:原先失败的 test_unreadable_or_conflicting_entries_fail_without_read_receipts 三个参数化全部通过;tests/test_inbox_pagination.py + tests/control_plane/test_inbox_read_recovery.py + tests/test_inbox_receipt_recovery.py 共 26 项通过;tests/control_plane_ts/inbox_read_transaction.test.ts 19 项通过;在合并前的同一树上另跑过 collaboration/manager/inbox 十四个测试族共 293 项通过;ruff 与 git diff --check 干净。
我的整体评价
结论 APPROVE。上一轮唯一的代码阻塞点(损坏目录丢失结构化 CLI 失败)已按「在原 IO 边界适配」的要求修复,并用原失败用例验证;边界没有扩大,也没有把异常吞成空结果。评审中另列的默认 SQLite 完整性能资格(冷暖 p50/p95 与 daemon 内存)本轮未提供,仍是独立证据缺口,不冒充通过。
English verdict: APPROVE - f70ffbe. The typed filesystem error is mapped back to OSError with its diagnostic code at the existing IO boundary, the previously failing corrupted-directory CLI test now passes, and 26 Python plus 19 TypeScript inbox tests pass; the SQLite performance qualification remains a separate evidence gap.
Goal And Delivered Outcome
main, merge base4f6e20a13b090318e14efeb20b91790e33fe694b.Author Declaration
Implemented against
Specification: the pinned RFC above. This is a review proposal; the
not_metrows hold approval and merge qualification.collaboration/inbox_read.tsScope And Continuation
The native owner performs source scans, pagination, per-record lifetime admission, existing typed view composition, and durable read receipts. It yields between bounded scan batches. Finalization rereads request sources under the existing cross-runtime request fence. Full legal source pages reuse the existing same-UID, digest-bound snapshot transport instead of truncating content or raising the ordinary wire budget.
Python retains registry/lifetime observation, real Core Todo and workspace IO, operation-ticket providers, command formatting and legacy output projection. Existing return-display file IO remains local in Python; this change does not add per-result RPCs there. Providers can have their own existing runtime calls: those must be counted in full-entry qualification, not hidden behind the two new transaction frames.
This is independently reversible: source addresses, public shapes and persisted receipt formats remain readable by the prior owners. It adds no queue, provider fallback, feature setting, sender grant, launch, promotion or deployment. The original-return transaction in #6855 remains separate. R1–R3 and the full asynchronous collaboration lifecycle remain open.
Migration economics
inbox_read.pytransport pluspending,_pending_result, receipt-read facades,returns, receiver phase wiring, command formatting and hook wiring; it also includes retained Host-fence/provider glue. It is an upper bound on pure transport, not a claim of zero bridge costThe small net increase buys complete source/receipt ownership, bounded scan fairness and legal large-page transport. It is not evidence of performance payoff by itself. Normal full-entry measurements are still an approval gate.
Validation
088d2f695d94051fc742ff46017b925b63b9d340. The 264-case source run used identical product files on024a12d3; the final base adds only peer-fixture UTF-8 decoding and a CI encoding setting. The affected peer suite was rerun after rebase.EffectRuntimeHostPermissionErrorbefore dispatch. Real HTTP bind denied with EPERM. Installed normal IPC/HTTP, Windows execution and complete default-provider performance remain unqualifiedThe new tests cover unchanged source bytes, full counts versus display limits, exact reads, page/error ordering, Goal-instance and actor conflicts, provider deferral, malformed records/receipts, legal >2 MiB output, event-loop availability, partial-process replay and late provider appearance. No live Goal, promotion, model job or external message was used.
Frontend / Visual Evidence
Type of Change
LoopX Area
Technical Direction
TypeScript transaction-payoff phase; existing R1–R3 consumers. No claim of full roadmap acceptance.
Shared-authority RFC fixture impact
Boundary Checklist