Repository navigation
refactor(cli): keep Codex host binding in the existing adapter owner - #7809
Conversation
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
loopx-agent
left a comment
There was a problem hiding this comment.
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
Reviewer: actor_kind=model_agent; model=gpt-6.1-sol; provider=OpenAI; runtime_reported; reasoning_effort=max
归属来自最近的宿主记录,bounded read 未验证当前执行活性;不据此认证模型权重或增加权限。
动机 / Motivation
准备发布版本的维护者,以及通过 CLI 启动 Codex 工作的调用方,是本次改动的受益者。 原 Turn 命令模块达到 1,022 行,超过原有 1,000 行限额;宿主参数绑定与工作结算交织,使版本无法通过这项检查。 将现有 Codex 参数适配移到已有 Host request 模块后,Turn 模块降为 988 行,原检查恢复通过,真实 CLI 的参数、会话与结算行为保持。 本 PR 只整理已有 CLI 适配边界,不调整预算、增加协议、配置或权限,也不把局部验证当成整个版本发布合格。 版本维护者仍需在最终合并源码上完成完整回归、安装、原模型组合、原生 Goal 及实际发布渠道读回。
改动思路 / Approach
现有 CLI Host request 模块负责参数和宿主绑定,规范决策仍归原 FirstParty admission 与 launch owner;这比增加一个决策层或拆散结算更容易维护。 本 PR 只移动有真实调用点的原适配闭包,保留原会话解析与整个结算路径,不引入新模块或第二份状态。
最强反对意见是:为了一个行数上限新增工厂函数,可能只是在隐藏规模。这里认可的是职责边界:参数适配可在原 request owner 中修改,Turn 的准入、执行、回写、费用与恢复仍保持完整。抬高阈值、登记例外或挤压排版不会解决这个分界。传入的 args、路径、Goal admission、当前 launch decision 和原 adapter 对象逐一对应;options、admission 和可选 operation import 均仍在实际调用时构造,没有提前做决定。
具体改动 / Changes
关键代码讲解
build_codex_cli_host_runner,第 81 行:返回原闭包。模型、sandbox、reasoning effort、MCP、显式 timeout 减五秒规则,以及 operation/普通 adapter 的分支、参数和诊断代码均未改。闭包 AST 与旧实现一致。execute_turn_run_once的绑定点,第 838 行:只把原内联定义替换为绑定调用;current launch decision 与 session resolver 仍留在原位置。除这个明确的调用替换外,整个原 Turn 函数 AST 一致,原 request parser 也一致。
规范为 docs/development/testing-and-quality.md#refactor-real-path-gate--重构真实路径门,spec_revision: 18949f5d3320fad1a825bc15966917bb0aa1d6aa,并沿用既有 Governed Turn 的宿主边界。
REAL-1:implemented。由原调用方列出普通与 operation、预览与执行、session resume、source/mirror、File/SQLite、Stop 和清理分支;同一原三模块在 base/head 各 266 项通过。REAL-2:implemented。原真实 public CLI 用例连续完成两个不同 Todo 并复用原 native session;经过实际合成 native 子进程和 File/TS authority,原 journal、任务与结算断言不变。实际 options 与 native 有效参数前后相同。REAL-3:implemented。在同一 public CLI 路径的进程内丢失明确提供的 model,原成功 oracle 失败;随后未变异的精确 head 通过。失败发生在 native 启动前,不声称变异抵达 native argv,也不把它当作模型执行。
差异为两文件 +61/-42,主模块 1,022→988 行,原 owner 129 行。检查了 base/head 的同类 DSH binding、原 request owner 和调用点;不增加新状态、CLI 或兼容版本。模块预算与例外表均未改。
对主干的风险 / Risks
最强回归是提取时丢参数、提前解析 admission,或让普通模式走入 operation 路径。原三模块在精确 head 全部通过;真实 File/SQLite operation 用例还覆盖 process/Turn Stop、source/mirror/resume、锁释放、失败回收和大 Unicode 输入。结构比较证明原 options 和延迟 import 没有漂移,公开 CLI 的丢参数反例说明验证能发现实际传输损失。注入的 adapter 在绑定前来自原别名;不承诺运行中任意重写 Python 模块全局变量的第三方 monkeypatch 行为。
PASS:base/head 各 266 项;两 Todo 实际 CLI 对照与预期失败敏感性;AST/参数映射;Ruff、配置内严格 mypy 17 文件、原模块/维护性检查、语义 advisory、双语差异、公开边界;标准 Goal premerge 五项直接/编译和五项选择检查。最初 premerge 的代码检查通过,但 CQR 因测试期间 main 前进而失配;已重建当前范围 receipt 并重新通过完整标准 gate。未查询、轮询或等待 PR CI。
主干同期合入 context first-read 与公开扫描修复;当前 head 与本 PR 两文件差异未变,它们不修改本次宿主参数/会话 owner。整版将在最终固定合并源码上重新验证。真实模型、原生 App 和其他平台的发布资格保持独立;可回退两文件,但原结构阻塞也会回来。
我的整体评价 / Verdict
problem_context: justified_increment;long_horizon preserved,user_experience preserved。原 host-neutral 生命周期、停止、验证、恢复和结算保持在原 owner,用户没有新增设置、确认或材料准备步骤。检查的完整差异仅有两个 Python CLI 文件,没有呈现内容、页面布局或 UI 交互改变;命令参数与失败恢复保持原入口,因此不需要新的首屏证据。
现有 CLI Host request 模块负责参数和宿主绑定,规范决策仍归原 FirstParty admission 与 launch owner;这比增加一个决策层或拆散结算更容易维护。 本 PR 只移动有真实调用点的原适配闭包,保留原会话解析与整个结算路径,不引入新模块或第二份状态。 这是相称、可独立回退的维护性修复,兼容边界为一起部署的内部 CLI 函数,不改变持久记录或独立消费者协议。当前 head 可在既有维护者授权及即时 readiness 通过后合并;版本维护者仍需在最终合并源码上完成完整回归、安装、原模型组合、原生 Goal 及实际发布渠道读回。
可复现的合成 CLI 参数观测与丢参数反例
在上述 base/head 使用相同测试环境保存下方脚本,正常命令预期通过;只在 head 执行变异命令,预期原成功断言失败。该脚本只观察测试夹具,不读取操作员数据。
python cli-host-options-observer.py --observation-file options.json tests/test_loopx_turn_driver.py::test_default_agent_session_reuses_planning_across_completed_todos -q
python cli-host-options-observer.py --observation-file negative.json --drop-model tests/test_loopx_turn_driver.py::test_default_agent_session_reuses_planning_across_completed_todos -q"""Observe an existing real CLI test; optional process-local field-loss sensitivity."""
import argparse, json, os, sys
from pathlib import Path
from unittest.mock import patch
parser=argparse.ArgumentParser()
parser.add_argument('--observation-file',type=Path,required=True)
parser.add_argument('--drop-model',action='store_true')
args,pytest_args=parser.parse_known_args()
sys.path.insert(0,str(Path.cwd()))
import pytest
from loopx.cli_commands import turn_run_once
class Observer:
def __init__(self): self.rows=[]
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_call(self,item):
original=turn_run_once.run_codex_cli_host
def observed(request,**kwargs):
if args.drop_model: kwargs['model']=None
self.rows.append({'options':{key:kwargs.get(key)for key in ['model','sandbox','reasoning_effort','mcp_server','timeout_seconds']},'admission_present':kwargs.get('goal_admission')is not None})
return original(request,**kwargs)
with patch.object(turn_run_once,'run_codex_cli_host',observed):
outcome=yield
if outcome.excinfo is not None: return
native=[json.loads(line)for line in Path(os.environ['FAKE_CODEX_LOG']).read_text().splitlines()]
self.native=[{'mode':'resume'if'resume'in row else'fresh','model':row[row.index('--model')+1]if'--model'in row else None,'native_session':'session-fixture-0001'in row}for row in native]
self.persist()
assert len(self.rows)==len(self.native)==2
assert all(row['model']=='fixture-model'for row in self.native), 'Actual native argv lost the model explicitly supplied to the public CLI'
def persist(self):
args.observation_file.write_text(json.dumps({'host_options':self.rows,'native_effective_arguments':getattr(self,'native',[]),'drop_model':args.drop_model},indent=2)+'\n')
observer=Observer()
try: code=pytest.main(pytest_args,plugins=[observer])
finally: observer.persist()
raise SystemExit(code)English verdict: APPROVE — Head c8b7a2a preserves the inspected CLI/Host behavior through real baseline/head paths and a sensitive independent oracle; full release qualification remains separate.
The existing
turn_run_once.pyhas grown to 1,022 lines and fails the unchanged 1,000-line command-module budget. This moves only the existing Codex CLI runner binding intoturn_host_request.py, the current CLI request/adapter owner. Turn orchestration becomes 988 lines; the adapter owner is 129 lines. No ceiling or exception changes.Options, admission construction and operation-host import still happen at launch, not factory creation. The canonical launch decision and session-binding resolver stay in place. The caller passes the same objects and the existing adapter alias, preserving normal execution and existing pre-execution injection points.
Validation at
c8b7a2a0b9ca57ae646f7ece0e2e6349a86066c4:18949f5d3320fad1a825bc15966917bb0aa1d6aaand this head. They cover real synthetic native subprocesses, File/SQLite authority, Stop at process/Turn boundaries, source/mirror/resume routes, lock release, cleanup, session reuse and durable settlement.cqr_0852c9f4f72566dfbe01. Standard Goal premerge passes: five direct/compile and five selected checks; no failures or manual holds. PR CI is not consulted under the current policy.Main advanced with the context-first-read and public-scanner changes during validation. This head and the two-file PR diff did not change; those upstream commits do not modify the host-option/session/launch owners. Their integration will be checked on the eventual fixed release source.
No rendered UI, optional feature, persisted format, permission or public CLI contract is changed. This is a source-path refactor proposal, not a full release or installed/native-model qualification. Remaining release gates and prior failures stay independent.