feat: 线程等待与状态读取 8 项(wait/read/message/handoff/worktree/pr-checks/automation/sleep)
对照 Codex app:wait_threads 首变+afterCursor+8上限、 read_thread 详情摘要、send_message_to_thread 复活失败任务、 handoff 登记与上报轮询、worktree 派生状态、PR checks JSON+ check-runs 日志与注释、automation GET/PATCH、clock.sleep (API+host)。诚实边界见 docs/thread-wait-ops.md:无 worktree 阶段流、无 automation 变化静默、无独立测试结果 记录、handoff 服务端不执行迁移、wait 仅数据库级变化检测。 三套件:cloud 940 OK(4 跳过)、host 91 OK、deploy 29 OK
This commit is contained in:
1 parent
38c0797cb3
commit
755029f220
8 files changed
+2535
No files matched your search
@@ -324,6 +324,65 @@ def cancel_automation(conn, automation_id, now=None):
|
||||
return True, automation_to_dict(row, now)
|
||||
|
||||
|
||||
def update_automation(conn, automation_id, fields, now=None):
|
||||
"""更新自动化定义(automation_update):fields 只认
|
||||
name / prompt / interval_seconds / repo_url 四个键,
|
||||
其它键一律忽略(不报错、不存入,避免调用方拼错字段
|
||||
名后以为已改);四个键全没给时抛 ValueError(空更新
|
||||
不是成功)。校验与创建同款(_validate_fields,名字
|
||||
仍按“同项目 active 名字唯一”查重,改名撞名时 400)。
|
||||
|
||||
返回 (ok, auto):行不存在时 (False, None);已取消时
|
||||
(False, 旧 dict)——取消是终态,改已取消的定义必须
|
||||
重建,不能靠 update 复活(与 cancel 的软删除语义
|
||||
一致)。成功时 commit。注意:更新不重置 last_run_at /
|
||||
last_claimed_at——改间隔只影响下一次到期判断(next_run
|
||||
由 last_run_at + 新间隔算),不立即触发一轮;想立即
|
||||
跑一轮应走执行器的 claim 路径,不是改定义。"""
|
||||
if now is None:
|
||||
now = time.time()
|
||||
ensure_automations_tables(conn)
|
||||
row = conn.execute(
|
||||
"SELECT * FROM automations WHERE id = ?", (automation_id,)
|
||||
).fetchone()
|
||||
if row is None:
|
||||
return False, None
|
||||
row = _row_dict(row, AUTOMATION_COLUMNS)
|
||||
if row["status"] != ACTIVE:
|
||||
return False, automation_to_dict(row, now)
|
||||
if not isinstance(fields, dict):
|
||||
raise ValueError("fields must be an object")
|
||||
current = automation_to_dict(row, now)
|
||||
name = fields.get("name", current["name"])
|
||||
prompt = fields.get("prompt", current["prompt"])
|
||||
interval = fields.get("interval_seconds",
|
||||
current["interval_seconds"])
|
||||
repo_url = fields.get("repo_url", current["repo_url"])
|
||||
if not any(k in fields for k in (
|
||||
"name", "prompt", "interval_seconds", "repo_url")):
|
||||
raise ValueError("no updatable field given")
|
||||
name, prompt, interval, repo_url = _validate_fields(
|
||||
name, prompt, interval, repo_url)
|
||||
if name != current["name"]:
|
||||
clash = conn.execute(
|
||||
"SELECT id FROM automations WHERE project_id = ?"
|
||||
" AND name = ? AND status = ? AND id != ?",
|
||||
(row["project_id"], name, ACTIVE, automation_id)
|
||||
).fetchone()
|
||||
if clash is not None:
|
||||
raise ValueError(
|
||||
"automation name already exists in this project")
|
||||
conn.execute(
|
||||
"UPDATE automations SET name = ?, prompt = ?,"
|
||||
" interval_seconds = ?, repo_url = ? WHERE id = ?",
|
||||
(name, prompt, interval, repo_url, automation_id))
|
||||
conn.commit()
|
||||
row = conn.execute(
|
||||
"SELECT * FROM automations WHERE id = ?", (automation_id,)
|
||||
).fetchone()
|
||||
return True, automation_to_dict(row, now)
|
||||
|
||||
|
||||
def due_automations(conn, project_id, now=None):
|
||||
"""列出某项目当前到期的 active 定义(纯读,不抢单)。
|
||||
SQL 层算到期:从未跑过(last_run_at 为空)或上次执行距
|
||||
|
||||
Reference in new issue
Block a user