Skip to content

fix(chat): stop owned background commands after completed answers - #6565

Merged
loopx-agent merged 3 commits into
mainfrom
codex/chat-completed-background-stop
Oct 15, 2026
Merged

loopx-agent merged 3 commits into
mainfrom
codex/chat-completed-background-stop

Conversation

@loopx-agent

@loopx-agent loopx-agent commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator

A completed ordinary Codex answer can leave its background command running. The original Lark topic /stop used to report an idle Session and leave that process writing files, including after a later answer.

For an idle ordinary Session, reuse the existing Core stop planner, native command ownership/readback and original request journal: discover live commands from canonical terminal Turns, freeze their exact identities before effects, stop only owned terminals, and preserve completed answer receipts. Retries retain the original targets. Active-Turn stop keeps its existing path and is independent of historical inventory availability; attached and native Goal control retain their separate contracts.

Validation at 3a6a5ba after normal main integration (9fc858c):

  • 460 related tests passed, including 11 new public positive/failure/retry/resume/replay cases, with real bounded child processes and synthetic provider/native wire.
  • An additional original Lark/Core/native counterfactual passed with 201 unrelated items, the owner on page three, reversed inventory order and absent process-local cache.
  • Exact-scope premerge: 5 direct and 14 selected checks passed; Ruff, Python compile, existing TypeScript effect execution, semantic drift and public boundary passed. Change-quality policy is disabled for the current Goal; CI is not consulted under its resolved policy.

Review found and fixed a regression in the initial version: unavailable older history could prevent current active interruption. The final idle guard and public regression preserve the original active stop. The first premerge run used a temporary directory nested in a Git worktree and broke a non-Git fixture; it is retained, and the full same-head gate passed in an independent non-Git namespace without changing assertions or limits.

This is source qualification. Installed-service adoption, actual native/model stop timing, the wider active-completion race and full community acceptance remain separate work; no production restart or new message consumer is included.

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 loopx-agent 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.

Reviewer: model_agent | gpt-6.1-sol | OpenAI | runtime_reported | effort=max

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

动机

在已授权飞书群话题中使用普通 Codex 助手的人,会在答复结束后仍遇到后台命令继续写文件。 例如第一条答复留下后台命令,用户又完成一次问答后发 /stop:旧行为把话题判为空闲并留下进程;新行为定位同会话旧命令并停止它,保留原答复和其他话题。 本次可验证的改进是原话题停止真正拥有的后台进程,并让失败的控制请求保持未确认;用户沿用原 /stop,无需重新提供会话信息或增加确认轮次。

本 PR 交付普通 Lark 话题的源码修复;不改变 attached 或原生 Goal 控制,不把源码测试当作已安装服务或完整社区准出的验收。 当前安装服务采用、真实模型长任务的停止时延、完成瞬间竞态和完整社区准出仍未在本 head 上验证,需由现有社区交付任务继续按原门禁处理。

改动思路

把目标冻结留在既有 TypeScript 会话请求决策器,把规范历史与 Codex 终端读取留在现有 Python provider 边界;复用原中断器和持久请求日志即可,不需要第二个消费者、取消注册表或新的可选模式。 这次只扩展空闲普通会话的原 Lark /stop,并修复初版对 active stop 的历史依赖;活跃停止保持原精确目标,线上采用、完整社区包和更广的完成竞态独立保留。

目标来自原话题授权、Core 完整规范历史以及原生 command item/terminal 读回。background_turn_ids 首次是派生观察;接受停止请求后,它成为必须冻结的原始意图:失败后重新选“当前最新任务”会改变用户原请求。沿用 target_recorded 日志,在效果之前写入,避免新建取消状态源。历史列表不可读会使空闲停止保持未确认,而不影响已在执行的精确停止。

以下为 head 3a6a5ba 的源码推导比较表,仅解释控制流,不代替运行证据:

原请求状态 旧主干 本 head
空闲,较早答复仍有后台命令 判为空闲、进程继续 冻结原 Turn → 停止拥有的终端 → 读回消失
当前仍在运行、旧历史不可读 沿原路径停止当前 Turn 同样沿原路径;初版523的历史依赖已修正
重试旧停止,已有新工作 使用既有冻结目标 保留原目标,不选择新 Session/Turn
清单或停止效果不可确认 无后台目标能力 原请求失败且不发送成功停止结论

具体改动

准确 head:3a6a5ba24b1350e472ac63cc2dce8dcafc953ee4;正常合入的主干基线:9fc858cbd8ee6d0268a4af11ed0f86e84ae6f755。7 文件 +351/-19,包含四个运行时文件、219 行公开回归、14 行 RFC 检查点和一条现有修复模式更新。

关键代码讲解

  1. ChatExternalConversations._admit_prepared:输入是原命令、原授权和当前 Session。只有首次空闲 /stop 才观察旧后台任务;先把 Core 计划写入原日志,再执行效果。当前 active Turn 直接走已有 interrupt 路径,历史故障不能挡住它;原请求的失败恢复继续使用已冻结目标。
  2. completed_background_turns:从完整规范消息找 terminal Core Turns,再验证原普通 Codex adapter/Thread 并与原生在线终端的 command item 求交集。返回 Core Turn ID;未知终端、其他话题、attached 和原生 Goal 控制均不能因此获得授权。
  3. planBoundConversationRequest:原 TypeScript owner 校验非空、唯一且绑定的 ID 列表,已记录目标优先于新的 Session/观察。其输出仍是现有 stop 操作与 response code;公开 Core 回放测试故意提供新 active/background,读回仍是原目标。
  4. CodexChatAgentSession.interrupt 与 background_turn_ids:共用完整分页 command-item/terminal 读取。中断先等选定原生 Turn 终态,再停止其命令拥有的 process,并重新读取直到消失;ACK、旧答复完成或列表缺页都不算停止成功。新增发现复用该 owner,既不终止整条 native Thread,也不重写已完成答复。

独立规范依据 spec_ref:docs/architecture/rfcs/app-conversation-and-async-inbox-v0.md;spec_revision:3bc74f9317479e36286414d899a77172a115e330(固定版本。逐项判定:Scope / Authority / Managed / Result 在这次有界源码路径实现并有原话题、进程、回放及失败读回覆盖;Correction/stop 的这条空闲补偿路径已验证,部署后真实时延及完成瞬间竞态仍待验;Attached 按既有分离合同保持范围外;Acceptance and failure matrix 的源码、安装、真实模型三层区分继续保留,后两层由现有社区任务跟进。RFC 新段落只披露这个增量,没有把它自己当作独立验收标准。

对主干的风险

最强反例是重试取消后来的工作,或者让旧历史故障阻断正在执行的停止。评审确实复现了第二项:初版523原有测试通过,但旧 item 读取失败会让 active Core 仍运行且未发送 native interrupt;恢复旧 routing/planning owner 反而成功。现在增加空闲条件和公开回归,在准确 head 上当前任务能中断,旧答复和外来真实子进程不变。此项已修复,没有留待下次。

当前四个关联模块 460 项通过(135.39 秒),包括新增 11 项公开用例。额外通过原 Lark/Core/native 路径检查:201 个无关 item/terminal、目标在第三页、反转顺序和删除内存缓存,原目标保持,实际仅 owned 子进程退出。停止失败后恢复原 provider 并重试、传输重连、其他 Topic/terminal、终态答复和未来目标回放均覆盖。这里的 provider/model/native wire 是合成边界,实际 bounded child 是真实进程;这些不是最新已安装 Bot 的实测结果。

准确作用域预合入检查最终 5 项直接检查、14 项选中检查全部通过,Ruff 和 Python 编译通过,既有 TypeScript effect-runtime 路径也实际执行。第一次把 TMPDIR 放在另一个 Git 工作区内,破坏 catalog-run-e2e 的“非 Git 临时目录”前提而失败;保留该错误,核对 runner 的 git probe 和 fixture 后,在独立非 Git 临时目录完整重跑通过,没有修改断言或阈值。早期旧基线的聚合超时和一次 BrokenPipe 清理警告也保留;准确主干环境本次 460 项无警告,不宣称已解释旧警告的全部原因。

语义与 CI 对齐

这是扩展既有请求日志和 idle stop 行为,复用已有 terminal states、typed Session ID 和 response code,没有新闭集、权限模型、消费者或配额义务。兼容只保留持久旧请求字段缺省为空,不能据此重新选择新工作。现有 self-repair 模式仍是按需建议,运行时完整读回则是实际强制条件。完整语义漂移及公开边界检查通过;开发期词汇探针的零候选只作提示,不证明语义等价。当前 Goal 的 change-quality policy 为 disabled,没有制造回执;已解析 wait_for_ci=false,因此未查询、轮询或等待 GitHub CI。

我的整体评价

结论是 justified_increment:long_horizon=improved,冻结和回放让原请求跨失败/重连继续得到真实结果;user_experience=improved,已有 /stop 有效且没有增加必要确认轮次。生产机制与问题成本相称,继续复用原 Core 和 provider owner 比建立新服务更小。persisted optional 字段具有真实回放必要性,当前不需要平行版本协议或额外配置开关。

本次源码修复、负例、集成和准确 head 评审已形成可独立回滚的结果;线上采用与实际模型时延、完整 golden/privacy/关键回合和社区两群准入仍是原任务的后续验收。授权合并不能代替授权部署,也不能把历史实测失败抹成通过。未发现此 head 的阻断问题;本记录为作者账户上的模型自评审,不能冒充独立维护者正式批准。

English verdict: APPROVE - 3a6a5ba stops owned completed-background commands through the original idle Lark Session while preserving active-stop availability, frozen replay targets and peer/receipt isolation. Exact-head local validation passes; installed adoption, live timing and full community acceptance remain separate holds.

@loopx-agent
loopx-agent merged commit a4f8de9 into main Oct 15, 2026
8 of 9 checks passed
@loopx-agent
loopx-agent deleted the codex/chat-completed-background-stop branch October 15, 2026 19:20

@loopx-agent loopx-agent 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.

Reviewer: model_agent | gpt-6.1-sol | OpenAI | runtime_reported | effort=max

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

Post-merge amendment: one newly exercised failure state supersedes my earlier universal replay-scope qualification. The earlier review and passing tests are retained; this does not rewrite the merge history.

动机

在已授权飞书群话题中使用普通 Codex 助手的人,会在答复结束后仍遇到后台命令继续写文件。 例如第一条答复留下后台命令,用户又完成一次问答后发 /stop:旧行为把话题判为空闲并留下进程;新行为定位同会话旧命令并停止它,保留原答复和其他话题。 本次可验证的改进是原话题停止真正拥有的后台进程,并让失败的控制请求保持未确认;用户沿用原 /stop,无需重新提供会话信息或增加确认轮次。 本 PR 交付普通 Lark 话题的源码修复;不改变 attached 或原生 Goal 控制,不把源码测试当作已安装服务或完整社区准出的验收。

原修复解决了新鲜空闲请求停止旧后台命令的问题,但补测发现恢复路径仍可误停新工作:第一次读取旧命令清单失败后,该原停止请求还没有记录目标;用户启动新任务,再重试旧请求,就会取消新任务而留下旧进程。这是实际反例,不能继续以整体回放安全作为已验证结论。Pre-inventory failure/retry violates original temporal stop scope; installed adoption and full community acceptance remain unqualified.

改动思路

仍复用既有TypeScript决策器、规范Session历史、原请求日志和Python原生IO边界;不另建服务或消费者,最小修复是把不可扩大的原始取消范围提前冻结。 本次复评6565同一源码head在首次原生观察失败后的恢复范围;公开保留已证正常路径与所有旧测试,当前阻断采用,后续在原社区任务修复并资格新head。

沿用原 Core 决策器、原 Session 规范历史、原请求日志和原生精确中断器仍是合适边界,不需要第二消费者、取消服务或新权限。需要把原 Session 和原规范取消范围的冻结移到可失败的原生清单读取之前;恢复只能在原范围内收窄,不能再次选择当前最新任务。已修好的 active stop 快路径必须保留,原完成答复和其他话题也必须保持。

这里区分两个真实状态:已有 target_recorded 的请求回放,原测试仍通过;原生观察先失败、尚未 target_recorded 的请求,是本次新增且失败的分支。两者不能用同一个绿色回放测试替代。

具体改动

本复评准确 head:3a6a5ba24b1350e472ac63cc2dce8dcafc953ee4,已合入 a4f8de9。源码未在这次补测中修改,仍是7文件 +351/-19。新的 P1 发现针对该完整 head,而不是对未来方案的假设。

关键代码讲解

  1. ChatExternalConversations._admit_prepared:空闲停止先调用 native completed_background_turns,成功之后才在258–261行记录 target_recorded、Session 和 Turn。首次 items/list 拒绝使 prepared 请求仍没有目标;重试会重读 current_session,并选择后来启动的 active Turn。
  2. completed_background_turns:其完整规范历史/原生拥有关系读取是必要的,但 provider IO 会失败。它不能成为持久原始停止意图写入之前的前提。应先保留原规范范围,再据完整原生事实验证或收窄。
  3. planBoundConversationRequest:已记录目标优先这条规则本身仍有效;问题是首次观察失败没有进入该分支。新反例中 Core 随重试把新 active ID 写入旧请求,随后既有 native interrupt 正确地执行了错误目标。

独立规范 spec_ref:docs/architecture/rfcs/app-conversation-and-async-inbox-v0.md,spec_revision:3bc74f9317479e36286414d899a77172a115e330。Scope、Authority 和 Correction/stop 在这个恢复状态为 not_met:原停止不能取得对后来工作的取消权。Managed 的已有中断实现与 Result 的成功新鲜路径证据保留;Attached 继续范围外。Acceptance and failure matrix 的源码、安装与真实模型验收区分保留,不能用当前源码合入关闭它。

对主干的风险

P1:首次 items/list 失败 → 旧停止请求没有冻结目标 → 原话题启动新 active Turn → 重试同一旧停止请求。真实生产 Lark/Core/TypeScript/native IO 路径、合成 provider/native wire 和实际有界子进程的反例,在2.83秒内失败。读回是:initial target_recorded=false、Session=null;retry Turn 等于新 Turn;新 Turn 已 interrupted,而旧 owned 进程和 foreign 进程仍存活。这些观测在测试清理之前保存,不是从异常文本推断。

此前准确 head 的460项关联测试、额外201条目/分页/倒序/缺cache用例,以及5项直接与14项选中预合入检查确实通过;现在明确它们没有覆盖“冻结之前失败,再启动新工作”的状态。本次新增失败必须保留,不能通过放宽断言、删除旧状态或只重跑已经绿色的 target_recorded 回放用例来消除。

最小修复是在原日志及原规范范围内提前冻结,失败保持原未确认请求;新增公开原话题回归覆盖新 active、新 Session、后续完成及恢复,并再次执行关联测试和准确作用域预合入检查。实际安装与原模型时延、原 goldens/隐私/社区准入仍未验;线上采用继续 HOLD,不重放任何真实失败用户 Turn。

语义与 CI 对齐

这是既有原停止范围合同的具体违反,复用已有 Session/Turn、target_recorded 和停止状态 vocabulary,不需要新闭集或扩大授权。current scope recovery 与语义资格现在判为 unintended_drift/violated。当前 Goal 的 wait_for_ci=false,未查询或等待 GitHub CI;这里是本地确定反例,不是缺少 CI 造成的证据空缺。原 change-quality disabled 状态也不能豁免这个失败。

我的整体评价

更新结论为 REQUEST_CHANGES;problem_context=not_yet_proven,long_horizon=regression,user_experience=regression。正常空闲停止的改进仍然存在,但旧请求误停新工作违反原任务的持续执行与恢复要求,因此不能保持完整 source adoption 资格。修复继续归现有社区任务和既有停止 owner,不新建平行项目;后续必须用当前反例证明旧进程停止、新任务与其他进程保留,再作新 head 资格和实际采用决定。

English verdict: REQUEST_CHANGES - 3a6a5ba can retarget a prior unconfirmed idle stop to newer active work after the first native inventory fails, while leaving its old owned process alive. Freeze original cancellation scope before fallible inventory IO, add the missing recovery counterexample, and hold adoption until the repaired exact head qualifies.

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