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
+27
-9
@@ -346,8 +346,15 @@ def extract_task_creates(text):
|
||||
executor = payload.get("executor")
|
||||
if executor is not None and executor not in EXECUTORS:
|
||||
continue
|
||||
# 旁聊(P2 G7):块里可显式指定 kind("main"|"side"),
|
||||
# 非法值整块跳过(与 executor 非法同款);不指定时
|
||||
# worker 按父任务 kind 继承(旁聊父 → 旁聊子)。
|
||||
kind = payload.get("kind")
|
||||
if kind is not None and kind not in ("main", "side"):
|
||||
continue
|
||||
out.append({"name": name, "prompt": prompt, "model": model,
|
||||
"effort": effort, "executor": executor})
|
||||
"effort": effort, "executor": executor,
|
||||
"kind": kind})
|
||||
return out
|
||||
|
||||
|
||||
@@ -1011,18 +1018,20 @@ def parent_spawn_generation(conn, parent_id):
|
||||
|
||||
只数 parent_task_id 非空且 coordinator_part 为空的祖先链(即
|
||||
派生链);协调员子任务(coordinator_part 非空)本身不是派生
|
||||
任务,代数为 0,链到此为止。走 32 步仍未到头时按已数到的算,
|
||||
绝不死循环。"""
|
||||
任务,代数为 0,链到此为止;旁聊任务(kind='side')同款——
|
||||
旁聊不是 marker 派生,链到旁聊为止。走 32 步仍未到头时按已数
|
||||
到的算,绝不死循环。"""
|
||||
generation = 0
|
||||
cur = parent_id
|
||||
for _ in range(32):
|
||||
if not cur:
|
||||
break
|
||||
row = conn.execute(
|
||||
"SELECT parent_task_id, coordinator_part FROM tasks"
|
||||
" WHERE id = ?", (cur,)).fetchone()
|
||||
"SELECT * FROM tasks WHERE id = ?", (cur,)).fetchone()
|
||||
if row is None:
|
||||
break
|
||||
if row_get(row, "kind") == "side":
|
||||
break
|
||||
parent = row_get(row, "parent_task_id")
|
||||
part = row_get(row, "coordinator_part")
|
||||
if parent is not None and part is None:
|
||||
@@ -1034,10 +1043,19 @@ def parent_spawn_generation(conn, parent_id):
|
||||
|
||||
|
||||
def count_direct_spawn_children(conn, parent_id):
|
||||
"""单个父任务的直接派生子任务数(不含协调员子任务)。"""
|
||||
row = conn.execute(
|
||||
"SELECT COUNT(*) FROM tasks WHERE parent_task_id = ?"
|
||||
" AND coordinator_part IS NULL", (parent_id,)).fetchone()
|
||||
"""单个父任务的直接派生子任务数(不含协调员子任务与旁聊)。"""
|
||||
try:
|
||||
row = conn.execute(
|
||||
"SELECT COUNT(*) FROM tasks WHERE parent_task_id = ?"
|
||||
" AND coordinator_part IS NULL"
|
||||
" AND (kind IS NULL OR kind != 'side')",
|
||||
(parent_id,)).fetchone()
|
||||
except Exception:
|
||||
# 老库/最小测试表缺 kind 列:当时不可能有旁聊行,
|
||||
# 不带 kind 条件的结果与带条件等价。
|
||||
row = conn.execute(
|
||||
"SELECT COUNT(*) FROM tasks WHERE parent_task_id = ?"
|
||||
" AND coordinator_part IS NULL", (parent_id,)).fetchone()
|
||||
try:
|
||||
return int(row[0]) if row is not None else 0
|
||||
except (TypeError, ValueError, IndexError):
|
||||
|
||||
Reference in new issue
Block a user