Skip to content

fix(chat): retire stopped quota failures from pending readback - #7791

Merged
loopx-agent merged 1 commit into
loopx-project:mainfrom
steven-kid:roadmap-auto/20261101-retired-quota-pending
Nov 1, 2026
Merged

loopx-agent merged 1 commit into
loopx-project:mainfrom
steven-kid:roadmap-auto/20261101-retired-quota-pending

Conversation

@steven-kid

Copy link
Copy Markdown
Collaborator

A quota-failed private Chat request remained in “Pending execution or reply” after the same source issued /stop or /new, even though recovery correctly refused new execution. This closes the readback gap reported in the post-merge audit of #7773, independently reproduced on main da4510af61277c0d0786fa5aa040ccb01538b1cb.

The existing TypeScript conversation owner now projects quota backlog from the original Turn, Session closure and durable stop observation. Native Chat shares its source-control reader between recovery and readback; Lark consumes that projection. Genuine quota waits remain visible regardless of backoff, exhausted retries or provider/binding availability. Failed Turns and notices remain intact. Recovery admission and exact admitted-continuation repair are unchanged.

This is a bounded reliability/readback outcome under the overall roadmap’s App/Lark stop-and-recovery journey, not completion of the controller RFC. It uses the built-in native_chat/Core owner and existing Lark extension, with no new capability, provider or persisted state. The adjacent refactor shares the source observation rather than creating a second retirement journal. #7430 still owns contract convergence; merged #7692/#7735 and open #7787/#7789 were checked to avoid duplicating their feature work.

Validation at c977cfdcb629bbf0bce1987cf630baebb6a0c144:

  • Same-source stop/new regression: both fail on the base (pending stays 1); final-head focused run: 5 passed.
  • Conversation/transport suites: 325 passed, 1 failed. The sole failure, test_app_long_return_keeps_all_parts_and_does_not_repeat_identical_parts, independently fails at the same 10-second drain assertion in a clean base worktree.
  • 35 TypeScript conversation tests passed, control-plane typecheck, repository-configured mypy, required Ruff scope, diff/compile checks and six-file public-boundary scan passed.
  • Tests cover foreign-source isolation, stop/new, subsequent independent work, restart, preserved failed history, no provider/model calls from health, closed/unknown Sessions and retry-exhaustion readback.
  • Standard premerge is not green: semantic-vocabulary, auto-research-quota-pause and auto-research-state-aware-wake bodies return 0 but their disposable managed-runtime cleanup reports “still exiting”. The identical selected premerge checks on a clean base reproduce all three cleanup failures. The maintainability ratchet also reports the untouched host_process_transport.run_host_process oversized-function finding (independently reproduced on base).

No frontend markup, settings or user flow changed: the existing private-conversation panel consumes the same pending_count. Desktop/narrow visual, real Lark/model, installed-app, full-repository pytest and performance qualification were not run. Extra local lifecycle reads are not claimed as a performance improvement. No merge or deployment is part of this proposal.

Signed-off-by: Yuchao Wang <stevenkid404@gmail.com>

@loopx-agent loopx-agent left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewer: model_agent; model=gpt-6.1-sol; provider=OpenAI; runtime_reported; reasoning_effort=max

动机 / Motivation

在飞书私聊中因模型额度不足而失败、随后已停止旧任务或开始新对话的用户。

旧失败请求已经被同一来源的停止或新对话命令取消,后台仍把它显示为待处理;本提交从原始会话、执行轮次和来源控制记录推导待处理状态,重启后旧项保持退出,新出现且尚未取消的额度失败仍会显示。

真实私聊 health 读回在 /stop 和 /new 后从 1 变为 0,后续独立工作和重启保持正确;未来的新额度失败回到 1,其他绑定的停止命令不能清除它。

本 PR 不改变自动重试准入、已接纳恢复的修复优先级或模型执行权限;本地真实存储与传输测试不证明在线飞书或付费模型调用。

改动思路 / Approach

私聊 health 仍先要求原来的失败通知已送达,并核对 binding、来源、消息、Turn 与 Session 的关联。它随后读取 Core 的既有请求记录、原始 Session/Turn 和事件,将事实交给既有 TS conversation binding owner,而不是在飞书适配器里复制业务规则。原本嵌在恢复判定中的来源停止关系被抽成同域 helper,供恢复与观察共用;观察结果不写入第二份取消标记。

这个区别影响实际等待语义:当前无法自动重试,可能只是绑定不可用、退避或达到自动重试上限,不能因此隐藏未完成请求;相反,用户已明确停止或开启新对话后,也不应继续显示旧额度失败。既有 context_recovery 分支、已接纳恢复修复的优先级和执行准入没有因 pending_observation 改变。下表是从源码与测试得出的行为比较,不是界面截图。

同一已送达额度失败的后续状态 基线计数 当前计数
同源后续 /stop 或 /new 1 0
仅当前不可重试,未取消 1 1
旧项取消后出现新额度失败 新失败仍应计入 1

待处理计数是投影,复用 Core 已有来源停止事实并由现有 TS 决策 owner 计算;重试准入继续保持独立,避免以是否能立即重试代替是否仍有未完成工作。

当前边界是停止或替换旧私聊失败后的持久读回,以及新失败继续可见;不增加恢复执行、调度或模型调用。

具体改动 / Changes

审查完整 head c977cfdcb629bbf0bce1987cf630baebb6a0c144,基于不可变 merge base da4510af61277c0d0786fa5aa040ccb01538b1cb。先读取该基线的既有规范,再检查全部生产、测试和文档改动;当前作者修改后的说明不充当独立验收依据。同时核对原始公开审计要求的同源取消、等待保留与无副作用读回。

规范映射:Frontend and Lark views → implemented:Same-audience projections retain canonical lineage and distinguish waiting work from terminal or superseded work.(paired /stop,/new base2fail/head2pass and restart);For private native Chat → implemented:Continuation requires original source identity and explicit lifecycle evidence; unknown availability is not cancellation.(88 Python/35TS + future/new/unrelated/missing-session counterfactuals);outcome-unknown → implemented:Readback must not replay model effects or rewrite retained failed history.(305 transport tests and no-provider/no-recovery observation fences)。

关键代码讲解

  1. CoreExternalConversations._source_stop_observed:原本 context recovery 中的停止事实被抽出复用。判定要求完整来源和 binding 匹配,且 /stop 或 /new 不早于原请求;不能用另一个来源的控制消息推断取消。读取的是完整 pending journal。

  2. CoreExternalConversations.quota_backlog_pending:新方法载入原始 Session、Turn 和 Turn events,使用既有来源停止事实请求 TS pending_observation。它不调用 provider/binding 恢复入口,也不将无法读取 Session 本身当作完成或取消证据。

  3. planBoundConversationRequest:新增观察分支区分“仍待处理”与“现在可自动重试”。匹配的 usage_limit_exceeded 失败在无取消事实时仍 pending;明确关闭的原 Session 或后续同源停止使其退出。context_recovery 的原执行分支继续保持独立。

  4. LarkPrivateConversations.health:该实际生产消费者保留已送达失败通知及所有身份关联检查,再委托 Core 获取布尔观察。相同公共计数字段被修正,既有 UI/manager 消费者无需新增配置或第二份状态。

对主干的风险 / Risks

最强反例是旧的停止命令把之后新出现的失败一起清掉,或者同一容器里的其他来源能取消目标请求。独立探针先通过真实私聊入口制造额度失败,完成同源 /stop 或 /new、后续独立工作与重启,再让同一来源产生时间更晚的新额度失败:旧项保持退出,新项计数为 1。随后加入 55 条其他 binding 的控制记录,计数仍为 1。临时让 Session 读回不可得时,已知失败 Turn 不会被当作不存在。对绑定解析和 context recovery 调用设置禁止栅栏后,health 仍成功;观察没有调用模型、恢复入口或改写任何失败 Turn。

PASS:tests/test_chat_conversation_bindings.py 88 项;TS conversation binding 35 项;三个私聊、ordinary project 和 manager returns 模块共 305 项;额外真实存储与来源范围探针 2 项。相同公开回归函数在不可变基线上 /stop 与 /new 两项均失败于 pending 1≠0,当前两项均通过,并覆盖重启和后续工作。Ruff、TS noEmit、配置的 mypy、diff 与双语 RFC diff 检查通过。

标准 premerge 执行 6 个直接检查与 14 个选中检查,13 项选中检查通过,另一项保留为 inherited advisory:control-plane-maintainability-ratchet-smoke.py 报告未修改的 run_host_process 函数过大。我在不可变基线上独立执行同一命令,其完整失败 stdout 与当前 head 一致,检查脚本和因果模块的 Git blob 也完全相同;没有提高预算或删掉该检查。该失败继续记录为失败,修复归原模块 owner,不作为本次待处理投影的回归。作者提到的旧 return-drain 超时在本轮 305 项当前测试中未出现,这不等于证明历史间歇问题已修复。

来源枚举使用完整请求 journal,而非显示上限;没有新增永久字段、模型准入、调度或配额义务。既有全量来源扫描的成本保留,不能从源码直觉推定性能改善或已发生性能回归。当前没有新的开关,也没有以 feature-off 名义掩盖默认读回修复。

语义与 CI 对齐

复用现有会话绑定及生命周期词汇,pending_observation 是同部署内部观察输入。协议新增的中英文段落说明取消与重试等待的差别;保留失败历史、原受众权限、既有退避与尝试上限,未把 pending 说成可执行义务。本轮只使用仓库原生本地验证,没有获取、轮询或等待远端 CI;未从已有 CI 提示推断通过、失败原因或合并权限。

我的整体评价 / Verdict

对停止后旧额度失败仍显示待处理的问题,交付判断为 goal_achieved。长期工作能力与用户体验均为 improved,限于取消旧等待、继续独立工作、重启后保持一致和后续新失败仍可见的实际路径;在线飞书、真实模型采用与长期效率收益仍未验证。新观察字段是本地投影输入,复用已有 TS owner 和来源生命周期语义,没有创建通用 Actor、Task 完成或重试权限。helper 抽取减少了恢复与 health 的规则漂移;没有理由为这个修复引入另一套状态或通用框架。

取消旧等待后可继续独立工作,重启不复活旧项,新失败仍保持可见;不以当前无法重试为完成依据。 已有授权与任务身份可直接复用,不增加确认、导航或重复输入;失败与取消后的读回可核验。

已检查 private_conversations.health、Core 观察入口和协议增补:既有待处理计数字段的语义修复,没有新增页面、控件或布局。

未触及模块的维护性失败仍保留。本次没有修改作者代码、合并或撤销旧 review;发布本结论不授予合并、付费执行、安装或 Goal 完成权限。若 head、来源边界或相关依赖改变,需要复核该新提交。

English verdict: APPROVE for exact head c977cfdcb629bbf0bce1987cf630baebb6a0c144; no blocking finding in the reviewed scope. The independently reproduced baseline maintainability failure remains advisory; live Lark/model adoption is unverified.

@loopx-agent
loopx-agent merged commit 8fd06e4 into loopx-project:main Nov 1, 2026
34 of 42 checks passed
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.

2 participants