Files
maestro/src/store/schema.sql
T
wangjia c89d6d129c feat: 重试与超时策略细化(可配 + 退避)tsk_N5wO3Armums2
## 变更摘要

### 1. 项目级可配重试上限与超时
- `Project` 新增 `maxRetries`(默认 2)和 `timeoutMs`(默认 30min = 1800000ms)字段
- SQLite schema:`projects` 表补 `max_retries` / `timeout_ms` 列(带 DEFAULT 的轻量迁移)
- `createProject` / `patchProject` 支持设置 / 校验新字段(maxRetries>=0, timeoutMs>=1000)
- API `POST /api/projects` 与 `PATCH /api/projects/:id` 透传新字段
- `runner.ts` 将 `project.timeoutMs` 传给 `runClaude`,不再固定 30min

### 2. 失败重试指数退避
- 编排器内存维护 `backoffUntil` Map(taskId → nextRetryAt),daemon 重启后清空
- 退避公式:`min(30s * 2^(attempt-1), 10min)`,第 1 次 30s / 第 2 次 60s / ...
- `claimable()` 过滤退避冷却中的任务
- `nowMs` 注入点(默认 `Date.now`)使测试可快进时钟验证退避行为

### 3. needs_attention 一键重投
- `Task` 新增 `retryBaseline` 字段(默认 0):记录上次重投时的失败 run 基线
- `Store.requeueTask(taskId)`:设置基线 = 当前失败 run 数 → 转 `queued`(仅限 needs_attention)
- 编排器用净失败数(`allFailed - retryBaseline`)判断是否已耗尽重试次数
- API `POST /api/tasks/:id/requeue` + MCP `requeue_task` 工具

### 4. 测试
- 重命名 `MAX_RETRIES` → `DEFAULT_MAX_RETRIES`,新增导出 `computeBackoffMs`
- 新增测试(共 +18):maxRetries=1/0、退避时序(精确 ms 边界)、重投后基线重置
- 迁移测试扩展:验证旧库补列后 maxRetries/timeoutMs/retryBaseline 使用默认值

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 04:04:41 +08:00

83 lines
3.9 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- maestro · SQLite schema v0.1(设计见 DESIGN.md §6/§9
PRAGMA journal_mode = WAL;
PRAGMA foreign_keys = ON;
CREATE TABLE IF NOT EXISTS projects (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
repo_path TEXT NOT NULL UNIQUE,
default_branch TEXT NOT NULL DEFAULT 'main',
verify_cmd TEXT,
autonomy TEXT NOT NULL DEFAULT 'manual', -- manual | auto-easy | auto-approved
model TEXT,
concurrency INTEGER NOT NULL DEFAULT 1,
max_retries INTEGER NOT NULL DEFAULT 2, -- 失败后最大自动重试次数
timeout_ms INTEGER NOT NULL DEFAULT 1800000, -- 单次执行超时毫秒(默认 30min)
status TEXT NOT NULL DEFAULT 'active', -- active | paused
created_at TEXT NOT NULL,
last_sync_at TEXT, -- 最近一次 todo.json 同步时间
logo TEXT, -- 自定义 logoURL / 仓库内相对路径;null=自动)
sort_order INTEGER NOT NULL DEFAULT 0 -- 侧栏排序(小在前)
);
CREATE TABLE IF NOT EXISTS tasks (
id TEXT PRIMARY KEY,
project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
parent_id TEXT REFERENCES tasks(id) ON DELETE CASCADE,
depth INTEGER NOT NULL DEFAULT 1, -- 1..4
title TEXT NOT NULL,
complexity TEXT NOT NULL, -- hard | medium | easy
status TEXT NOT NULL DEFAULT 'init',
priority INTEGER NOT NULL DEFAULT 1, -- P0 最高 / P1 中 / P2 最低
deps TEXT NOT NULL DEFAULT '[]', -- JSON array of task ids
plan TEXT, -- Hard:分析 + 拆解
spec TEXT, -- Medium:改动 + 理由
operations TEXT, -- Easy:将执行的操作
result TEXT, -- JSON TaskResult
assignee TEXT, -- agent | human
retry_baseline INTEGER NOT NULL DEFAULT 0, -- 上次手动重投时已有的失败 run 数(重置重试计数用)
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
source_ref TEXT -- 旧 todo 来源标识(todo:17 / todo:17/1A),项目内唯一
);
CREATE INDEX IF NOT EXISTS idx_tasks_project ON tasks(project_id, status);
CREATE INDEX IF NOT EXISTS idx_tasks_parent ON tasks(parent_id);
CREATE INDEX IF NOT EXISTS idx_tasks_source ON tasks(project_id, source_ref);
CREATE TABLE IF NOT EXISTS approvals (
id TEXT PRIMARY KEY,
task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
gate TEXT NOT NULL, -- plan | spec | exec
action TEXT NOT NULL, -- accept | reject
actor TEXT NOT NULL,
reason TEXT, -- reject 必填
at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_approvals_task ON approvals(task_id, at);
CREATE TABLE IF NOT EXISTS runs (
id TEXT PRIMARY KEY,
task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
kind TEXT NOT NULL, -- planner | executor | reviewer | security
worktree TEXT,
branch TEXT,
status TEXT NOT NULL, -- started | succeeded | failed | cancelled
started_at TEXT NOT NULL,
ended_at TEXT,
transcript_ref TEXT,
claude_session_id TEXT,
error TEXT
);
CREATE INDEX IF NOT EXISTS idx_runs_task ON runs(task_id, started_at);
-- 追加型事件日志:审计 + 实时看板推送源
CREATE TABLE IF NOT EXISTS events (
id TEXT PRIMARY KEY,
project_id TEXT NOT NULL,
task_id TEXT,
type TEXT NOT NULL,
payload TEXT NOT NULL DEFAULT '{}',
at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_events_project ON events(project_id, at);