auto-sync: 2026-05-18 11:36:11
This commit is contained in:
+10
-90
@@ -14,11 +14,9 @@ def init_db(db_path: Path) -> None:
|
||||
try:
|
||||
for stmt in _SCHEMA_STATEMENTS:
|
||||
conn.execute(stmt)
|
||||
for stmt in _CARDS_SCHEMA:
|
||||
conn.execute(stmt)
|
||||
# v2.6.1 迁移:旧 DB 可能缺少新字段
|
||||
_migrate_v261(conn)
|
||||
# v2.7 迁移:cards 表 + card_id + stage
|
||||
# v2.7 迁移:stages_json
|
||||
_migrate_v27(conn)
|
||||
conn.commit()
|
||||
finally:
|
||||
@@ -41,70 +39,11 @@ def _migrate_v261(conn: sqlite3.Connection) -> None:
|
||||
|
||||
|
||||
def _migrate_v27(conn: sqlite3.Connection) -> None:
|
||||
"""v2.7 数据迁移:cards 表 + 所有表加 card_id/stage"""
|
||||
# 1. cards 表(CREATE IF NOT EXISTS 已处理)
|
||||
for stmt in _CARDS_SCHEMA:
|
||||
try:
|
||||
conn.execute(stmt)
|
||||
except sqlite3.OperationalError:
|
||||
pass
|
||||
|
||||
# 2. 所有表加 card_id
|
||||
for table in ("tasks", "outputs", "reviews", "comments", "events",
|
||||
"task_attempts", "routing_decisions"):
|
||||
_safe_add_column(conn, table, "card_id", "TEXT")
|
||||
|
||||
# 3. tasks 加 stage
|
||||
"""v2.7 数据迁移:stages_json 字段(动态 Stage 定义)"""
|
||||
_safe_add_column(conn, "tasks", "stages_json", "TEXT DEFAULT '[]'")
|
||||
# 确保 stage 列存在(旧 v2.7 迁移可能已添加)
|
||||
_safe_add_column(conn, "tasks", "stage", "TEXT")
|
||||
|
||||
# 4. card_id 索引
|
||||
for table in ("tasks", "events"):
|
||||
try:
|
||||
conn.execute(
|
||||
f"CREATE INDEX IF NOT EXISTS idx_{table}_card ON {table}(card_id)")
|
||||
except sqlite3.OperationalError:
|
||||
pass
|
||||
|
||||
# 5. 回填 card_id = 'default'(分批,避免大表锁太久)
|
||||
for table in ("tasks", "outputs", "reviews", "comments", "events",
|
||||
"task_attempts", "routing_decisions"):
|
||||
try:
|
||||
row = conn.execute(
|
||||
f"SELECT COUNT(*) as cnt FROM {table} WHERE card_id IS NULL"
|
||||
).fetchone()
|
||||
if row and row["cnt"] > 0:
|
||||
# 分批更新,每批 500 行
|
||||
_batch_update_card_id(conn, table, "default", batch_size=500)
|
||||
except sqlite3.OperationalError:
|
||||
pass # 表不存在则跳过
|
||||
|
||||
# 6. 创建 default Card(如果不存在)
|
||||
try:
|
||||
existing = conn.execute(
|
||||
"SELECT id FROM cards WHERE id='default'"
|
||||
).fetchone()
|
||||
if not existing:
|
||||
conn.execute(
|
||||
"INSERT INTO cards (id, name, status, card_type, stages_json, labels_json, created_at) "
|
||||
"VALUES ('default', 'Default', 'active', 'default', '[]', '[]', datetime('now'))"
|
||||
)
|
||||
except sqlite3.OperationalError:
|
||||
pass # cards 表不存在则跳过(不应该发生)
|
||||
|
||||
|
||||
def _batch_update_card_id(conn: sqlite3.Connection, table: str,
|
||||
card_id: str, batch_size: int = 500) -> None:
|
||||
"""分批更新 card_id,避免大表长时间锁"""
|
||||
while True:
|
||||
cursor = conn.execute(
|
||||
f"UPDATE {table} SET card_id=? WHERE rowid IN ("
|
||||
f"SELECT rowid FROM {table} WHERE card_id IS NULL LIMIT {batch_size})",
|
||||
(card_id,),
|
||||
)
|
||||
if cursor.rowcount == 0:
|
||||
break
|
||||
conn.commit()
|
||||
|
||||
|
||||
def _safe_add_column(conn: sqlite3.Connection, table: str,
|
||||
column: str, col_type: str) -> None:
|
||||
@@ -141,6 +80,9 @@ VALID_STATUSES = frozenset({
|
||||
|
||||
TERMINAL_STATUSES = frozenset({"done", "cancelled"})
|
||||
|
||||
# 手动状态(不参与聚合推导)
|
||||
MANUAL_STATUSES = frozenset({"cancelled"})
|
||||
|
||||
VALID_TRANSITIONS = {
|
||||
"pending": {"claimed", "cancelled"},
|
||||
"claimed": {"working", "pending", "cancelled"},
|
||||
@@ -188,8 +130,7 @@ ATTEMPT_OUTCOMES = frozenset({
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Schema - split into individual statements to avoid SQLite 3.51 CHECK bug
|
||||
# (table-level CHECK as non-last column definition causes parse error)
|
||||
# Schema
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
_SCHEMA_STATEMENTS = [
|
||||
@@ -220,8 +161,8 @@ _SCHEMA_STATEMENTS = [
|
||||
current_agent TEXT,
|
||||
previous_agent TEXT,
|
||||
next_capability TEXT,
|
||||
card_id TEXT DEFAULT 'default',
|
||||
stage TEXT
|
||||
stage TEXT,
|
||||
stages_json TEXT DEFAULT '[]'
|
||||
)""",
|
||||
"CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_tasks_assignee ON tasks(assignee)",
|
||||
@@ -386,24 +327,3 @@ _SCHEMA_STATEMENTS = [
|
||||
"CREATE INDEX IF NOT EXISTS idx_routing_mode ON routing_decisions(mode)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_routing_time ON routing_decisions(created_at)",
|
||||
]
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Cards Schema (v2.7)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
_CARDS_SCHEMA = [
|
||||
"""CREATE TABLE IF NOT EXISTS cards (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT DEFAULT '',
|
||||
card_type TEXT DEFAULT 'default' CHECK (card_type IN ('default','strategy','data_pipeline','research','platform','bugfix','mail')),
|
||||
stages_json TEXT DEFAULT '[]',
|
||||
labels_json TEXT DEFAULT '[]',
|
||||
status TEXT DEFAULT 'active' CHECK (status IN ('active','pending','working','review','done','failed','blocked','cancelled','archived','deleted')),
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT,
|
||||
archived_at TEXT
|
||||
)""",
|
||||
"CREATE INDEX IF NOT EXISTS idx_cards_id ON cards(id)",
|
||||
"CREATE INDEX IF NOT EXISTS idx_cards_status ON cards(status)",
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user