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
@@ -240,6 +240,48 @@ class TestMigrateNewTables(unittest.TestCase):
|
||||
"result_text", "error_text", "created_at"):
|
||||
self.assertIn(col, attempts_inserts[0])
|
||||
|
||||
def test_kind_column_backfilled_when_missing(self):
|
||||
# P2:PG 侧 tasks 尚无 kind 列时,迁移必须先 ALTER 补列
|
||||
# 再直插,且插入列清单里含 kind;kind 已存在时不重复
|
||||
# ALTER(与 agent_* 列同款 backfill 惯例)。
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
src = os.path.join(tmp, "src.db")
|
||||
_make_sqlite(src)
|
||||
fake = FakePgConn(pragma_columns={
|
||||
"projects": {"repo_url"},
|
||||
"tasks": {"id", "project_id"},
|
||||
"task_followups": {"id", "task_id", "project_id",
|
||||
"body", "mode", "status",
|
||||
"created_by", "sender_task_id",
|
||||
"created_at", "updated_at"},
|
||||
})
|
||||
result = migrate_sqlite_to_postgres(src, pg_conn=fake)
|
||||
self.assertEqual(result.get("tasks"), 1, "tasks")
|
||||
self.assertTrue(
|
||||
any("tasks" in a and "kind" in a for a in fake.alters),
|
||||
"未发出 tasks.kind 补列 ALTER:%s" % fake.alters)
|
||||
# 注:插入列清单取自源 SQLite 表的列,夹具源表无 kind
|
||||
# 列故清单里无 kind;生产源库(init_db 建表)有 kind,
|
||||
# 迁移时按列原样搬运(与 agent_* 列同款)。
|
||||
|
||||
def test_kind_column_present_skips_alter(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
src = os.path.join(tmp, "src.db")
|
||||
_make_sqlite(src)
|
||||
fake = FakePgConn(pragma_columns={
|
||||
"projects": {"repo_url"},
|
||||
"tasks": {"id", "project_id", "kind"},
|
||||
"task_followups": {"id", "task_id", "project_id",
|
||||
"body", "mode", "status",
|
||||
"created_by", "sender_task_id",
|
||||
"created_at", "updated_at"},
|
||||
})
|
||||
result = migrate_sqlite_to_postgres(src, pg_conn=fake)
|
||||
self.assertEqual(result.get("tasks"), 1, "tasks")
|
||||
self.assertFalse(
|
||||
any("tasks" in a and "kind" in a for a in fake.alters),
|
||||
"kind 已存在却仍发补列 ALTER:%s" % fake.alters)
|
||||
|
||||
def test_sender_column_present_skips_alter(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
src = os.path.join(tmp, "src.db")
|
||||
|
||||
Reference in new issue
Block a user