Tasks gain kind='main'|'side' (NULL=main). A side thread hangs under a parent task, defaults to the local executor, never builds a worktree, branch or container unless explicitly asked, and writes its conclusion back to the parent via task_followups on terminal transition. - server: kind validation + side-thread gates (parent required, no coordinator_parts/upload_id/environment_id, parent must not be a coordinator), local executor default, GET /tasks kind filter, /worktree 404 and /fork /merge /pr 409 for side threads. - worker: side-default shunt in _run_attempt (no-worktree scratch cwd, in-memory plan=1 when a repo is present, seed/checkpoint skipped), _writeback_side_thread, marker task-create kind field + inheritance, side threads excluded from marker spawn counts. - host/sandbox: --no-worktree plumbing for dispatch/goal/popen (Local + Docker), scratch cwd work/side-threads/<task-id>. - console (classic only): kind filter + [旁聊] rows + '开旁聊' entry, side-thread dispatch mode, side-thread list in detail, merge/PR/fork hidden for side threads; build exit 0, console.html sha df39db5a. - tests: new test_sidethread.py (29 tests) + kind backfill tests in test_p2_migrations.py; full cloud suite 3900 tests OK (skipped=13). Design: verification/codex-muse-role-alignment-design-20261108.md Summary: verification/codex-muse-role-align-p1p2-20261108.md Local only: not pushed, not merged, not deployed.
This commit is contained in:
1 parent
f6df76ee8f
commit
7983567491
24 files changed
+1244
-100
No files matched your search
+28
-7
@@ -592,7 +592,7 @@ class LocalExecutor(Executor):
|
||||
return re.sub(r"[^A-Za-z0-9_.-]", "-", raw)
|
||||
|
||||
def dispatch_task(self, task, seed_dir=None, extra_env=None,
|
||||
model_provider=None) -> dict:
|
||||
model_provider=None, no_worktree=False) -> dict:
|
||||
"""调 host.py dispatch:建 host 任务并跑一轮 turn。
|
||||
|
||||
命令:host.py [--config CFG] [--model M] dispatch --name <host 名>
|
||||
@@ -605,6 +605,8 @@ class LocalExecutor(Executor):
|
||||
model 换成 fallback 模型,第二次 dispatch/run 就跑在新模型上)。
|
||||
seed_dir 非空时追加 --seed-dir:host.py 在建 worktree 后、
|
||||
跑 turn 前把暂存附件拷进去(run 续跑不走这条路,不重复拷)。
|
||||
no_worktree(旁聊默认):追加 --no-worktree,host.py 不建
|
||||
worktree/分支,无 repo 时以 per-task scratch 目录为 cwd。
|
||||
"""
|
||||
if not isinstance(task, dict) or task.get("id") is None:
|
||||
raise ValueError("任务缺少 id")
|
||||
@@ -625,6 +627,8 @@ class LocalExecutor(Executor):
|
||||
cmd += ["--repo", repo]
|
||||
if seed_dir:
|
||||
cmd += ["--seed-dir", seed_dir]
|
||||
if no_worktree:
|
||||
cmd += ["--no-worktree"]
|
||||
return self._result_from_runner(
|
||||
task["id"], self._run_local(cmd, extra_env))
|
||||
|
||||
@@ -673,7 +677,7 @@ class LocalExecutor(Executor):
|
||||
task["id"], self._run_local(cmd, extra_env))
|
||||
|
||||
def goal_task(self, task, seed_dir=None, extra_env=None,
|
||||
model_provider=None) -> dict:
|
||||
model_provider=None, no_worktree=False) -> dict:
|
||||
"""调 host.py goal:目标循环(turn→验测→喂回重试)。
|
||||
|
||||
命令:host.py [--config CFG] [--model M] goal --name <host 名>
|
||||
@@ -720,6 +724,8 @@ class LocalExecutor(Executor):
|
||||
cmd += ["--repo", repo]
|
||||
if seed_dir:
|
||||
cmd += ["--seed-dir", seed_dir]
|
||||
if no_worktree:
|
||||
cmd += ["--no-worktree"]
|
||||
return self._goal_result_from_runner(
|
||||
task["id"], self._run_local(cmd, extra_env))
|
||||
|
||||
@@ -867,7 +873,8 @@ class LocalExecutor(Executor):
|
||||
}
|
||||
|
||||
def popen_task(self, task, kind="dispatch", seed_dir=None,
|
||||
extra_env=None, model_provider=None):
|
||||
extra_env=None, model_provider=None,
|
||||
no_worktree=False):
|
||||
"""进程组启动 host.py 子进程,返回 Popen(供真取消用)。
|
||||
|
||||
start_new_session=True 让子进程自成进程组,worker 取消时
|
||||
@@ -920,6 +927,8 @@ class LocalExecutor(Executor):
|
||||
cmd += ["--repo", repo]
|
||||
if seed_dir:
|
||||
cmd += ["--seed-dir", seed_dir]
|
||||
if no_worktree:
|
||||
cmd += ["--no-worktree"]
|
||||
elif kind == "goal-resume-breakpoint":
|
||||
# G10 断点:无 --goal-text,不开新循环;host 侧按
|
||||
# goal_rounds_done 的下一轮接着跑,轮次上限沿用原
|
||||
@@ -949,6 +958,8 @@ class LocalExecutor(Executor):
|
||||
cmd += ["--repo", repo]
|
||||
if seed_dir:
|
||||
cmd += ["--seed-dir", seed_dir]
|
||||
if no_worktree:
|
||||
cmd += ["--no-worktree"]
|
||||
if extra_env:
|
||||
merged = dict(os.environ)
|
||||
merged.update(extra_env)
|
||||
@@ -1505,13 +1516,17 @@ class DockerExecutor(Executor):
|
||||
|
||||
def dispatch_task(self, task, timeout=None, extra_mounts=None,
|
||||
network=None, extra_env=None,
|
||||
seed_host_dir=None, model_provider=None) -> dict:
|
||||
seed_host_dir=None, model_provider=None,
|
||||
no_worktree=False) -> dict:
|
||||
"""容器内调 host.py dispatch:建 host 任务并跑一轮 turn。
|
||||
|
||||
seed_host_dir 非空时把暂存目录只读挂载进容器
|
||||
(/tmp/codex-seed),内层 host.py 用 --seed-dir
|
||||
/tmp/codex-seed 拷进 worktree;挂载整个目录(不是单个
|
||||
文件)是为了一包多文件与子目录。"""
|
||||
文件)是为了一包多文件与子目录。no_worktree(旁聊):
|
||||
内层命令追加 --no-worktree,容器内 host.py 不建
|
||||
worktree/分支(worker 只在本地默认旁聊上接线,显式
|
||||
docker 旁聊走既有语义;此处只做透传)。"""
|
||||
if not isinstance(task, dict) or task.get("id") is None:
|
||||
raise ValueError("任务缺少 id")
|
||||
if not task.get("prompt"):
|
||||
@@ -1530,6 +1545,8 @@ class DockerExecutor(Executor):
|
||||
if seed_host_dir:
|
||||
inner += ["--seed-dir", "/tmp/codex-seed"]
|
||||
mounts.append("%s:/tmp/codex-seed:ro" % seed_host_dir)
|
||||
if no_worktree:
|
||||
inner += ["--no-worktree"]
|
||||
return self._run_in_container(task, inner, timeout=timeout,
|
||||
extra_mounts=mounts or None,
|
||||
network=network,
|
||||
@@ -1573,11 +1590,13 @@ class DockerExecutor(Executor):
|
||||
|
||||
def goal_task(self, task, timeout=None, extra_mounts=None,
|
||||
network=None, extra_env=None,
|
||||
seed_host_dir=None, model_provider=None) -> dict:
|
||||
seed_host_dir=None, model_provider=None,
|
||||
no_worktree=False) -> dict:
|
||||
"""容器内调 host.py goal:目标循环(turn→验测→喂回重试)。
|
||||
|
||||
seed_host_dir 语义同 dispatch_task(只读挂载 + 内层
|
||||
--seed-dir /tmp/codex-seed)。
|
||||
--seed-dir /tmp/codex-seed)。no_worktree 语义同
|
||||
dispatch_task(内层命令追加 --no-worktree)。
|
||||
|
||||
不走 _run_in_container:它经 _parse_host_result 截头
|
||||
stdout,而 goal 结论在末尾,截头会把结论截掉;这里直接
|
||||
@@ -1618,6 +1637,8 @@ class DockerExecutor(Executor):
|
||||
if seed_host_dir:
|
||||
inner += ["--seed-dir", "/tmp/codex-seed"]
|
||||
mounts.append("%s:/tmp/codex-seed:ro" % seed_host_dir)
|
||||
if no_worktree:
|
||||
inner += ["--no-worktree"]
|
||||
cmd = self.build_run_command(task, inner,
|
||||
extra_mounts=mounts or None,
|
||||
network=network,
|
||||
|
||||
Reference in new issue
Block a user