refactor(todo): 重构待办事项管理逻辑及更新状态规则
- 移除 TodoItem 中的 priority、created_at 和 updated_at 字段 - 强制每个任务都必须有唯一 id,且由用户负责生成 - 修改合并模式逻辑,merge=true 下保留未提及的旧任务 - 支持已完成和已取消任务重新激活(状态改回 pending 或 in_progress) - 禁止 in_progress 状态退回到 pending,必须标记为 completed 或 cancelled - 优化状态转换校验,允许特定状态间合法切换 - 简化任务变更消息,移除详细的新增/更新/移除统计 - 更新文档和示例,明确 id 必须由用户生成和使用 - 修复和补充测试,增强状态转换和合并模式验证 - 调整任务时间戳生成逻辑,统一使用当前时间及索引 - 该变更提供更合理的任务状态机械及管理模式,提升稳定性和易用性
This commit is contained in:
parent
02172b6065
commit
229221aab1
@ -76,9 +76,6 @@ impl CommandHandler for ListTodosCommandHandler {
|
||||
id: r.id,
|
||||
content: r.content,
|
||||
status: r.status,
|
||||
priority: r.priority,
|
||||
created_at: r.created_at,
|
||||
updated_at: r.updated_at,
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
||||
@ -141,9 +141,15 @@ impl BusToolCallEmitter {
|
||||
|
||||
let topic_id = self.metadata.get("topic_id").filter(|t| !t.is_empty()).cloned();
|
||||
|
||||
let now = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.unwrap_or_default()
|
||||
.as_secs() as i64;
|
||||
|
||||
let records: Vec<crate::storage::TodoRecord> = todos_array
|
||||
.iter()
|
||||
.filter_map(|item| {
|
||||
.enumerate()
|
||||
.filter_map(|(idx, item)| {
|
||||
Some(crate::storage::TodoRecord {
|
||||
id: item.get("id")?.as_str()?.to_string(),
|
||||
scope_key: scope_key.clone(),
|
||||
@ -151,9 +157,9 @@ impl BusToolCallEmitter {
|
||||
topic_id: topic_id.clone(),
|
||||
content: item.get("content")?.as_str()?.to_string(),
|
||||
status: item.get("status")?.as_str()?.to_string(),
|
||||
priority: item.get("priority")?.as_str()?.to_string(),
|
||||
created_at: item.get("created_at")?.as_i64()?,
|
||||
updated_at: item.get("updated_at")?.as_i64()?,
|
||||
priority: "medium".to_string(),
|
||||
created_at: now + idx as i64,
|
||||
updated_at: now,
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
@ -28,7 +28,7 @@ const TODO_WRITE_INSTRUCTIONS: &str = r#"
|
||||
|
||||
### merge 参数
|
||||
- `merge: false`(默认):全量替换 — 只传入需要追踪的 todo,不在列表中的项将被移除
|
||||
- `merge: true`(推荐):增量更新 — 只传入需要添加或更新的项,未提及的项保持不变。**绝大多数情况应该使用 merge=true,这样你不需要记住所有 id**
|
||||
- `merge: true`(推荐):增量更新 — 只传入需要添加或更新的项,未提及的项保持不变。**绝大多数情况应该使用 merge=true**
|
||||
|
||||
### 状态语义
|
||||
- `pending` — 尚未开始
|
||||
@ -39,33 +39,34 @@ const TODO_WRITE_INSTRUCTIONS: &str = r#"
|
||||
### 核心规则
|
||||
1. 同一时间只能有一个任务处于 `in_progress` 状态
|
||||
2. 必须先完成当前 `in_progress` 的任务,再开始下一个
|
||||
3. `completed` 和 `cancelled` 是终端状态,已完成的项不能被重新激活
|
||||
4. 不要先标记 completed 再去实际执行 — 先完成工作,再标记
|
||||
5. `content` 字段保持简洁、可执行
|
||||
6. **更新已有任务必须传 `id`**。新任务不传 id(工具会自动生成),更新状态时必须传 id。id 可以从之前 todo_write 返回的 `current_todos` 中获取
|
||||
3. `completed` 和 `cancelled` 的项可以重新激活(改回 `in_progress` 或 `pending`),用于任务返工或恢复
|
||||
4. `in_progress` 不能退回 `pending`,应直接标记为 `completed` 或 `cancelled`
|
||||
5. 不要先标记 completed 再去实际执行 — 先完成工作,再标记
|
||||
6. `content` 字段保持简洁、可执行
|
||||
7. **每个任务都必须传 `id`**。新任务由你生成一个短随机字符串作为 id(如 `"r9Tg8Kq2"`),更新任务时使用相同的 id。id 可以从之前 todo_write 返回的 `current_todos` 中获取
|
||||
|
||||
### 使用范例
|
||||
|
||||
创建新任务(新任务必须 pending 或 in_progress,不传 id):
|
||||
创建新任务(生成随机 id):
|
||||
```json
|
||||
{"merge": true, "todos": [{"content": "修复登录 bug", "status": "in_progress"}]}
|
||||
{"merge": true, "todos": [{"id": "aB3kLm9x", "content": "修复登录 bug", "status": "in_progress"}]}
|
||||
```
|
||||
|
||||
追加新任务:
|
||||
```json
|
||||
{"merge": true, "todos": [{"content": "补充测试", "status": "pending"}]}
|
||||
{"merge": true, "todos": [{"id": "pQ7nWy2z", "content": "补充测试", "status": "pending"}]}
|
||||
```
|
||||
|
||||
更新已有任务(**必须传 id**,从上次返回的 current_todos 中取得):
|
||||
更新已有任务(使用创建时的 id):
|
||||
```json
|
||||
{"merge": true, "todos": [{"id": "abc-123", "content": "修复登录 bug", "status": "completed"}]}
|
||||
{"merge": true, "todos": [{"id": "aB3kLm9x", "content": "修复登录 bug", "status": "completed"}]}
|
||||
```
|
||||
|
||||
同时更新多项:
|
||||
```json
|
||||
{"merge": true, "todos": [
|
||||
{"id": "abc-123", "content": "修复登录 bug", "status": "completed"},
|
||||
{"content": "代码审查", "status": "in_progress"}
|
||||
{"id": "aB3kLm9x", "content": "修复登录 bug", "status": "completed"},
|
||||
{"id": "pQ7nWy2z", "content": "补充测试", "status": "in_progress"}
|
||||
]}
|
||||
```
|
||||
"#;
|
||||
|
||||
@ -88,9 +88,6 @@ pub struct TodoItemSummary {
|
||||
pub id: String,
|
||||
pub content: String,
|
||||
pub status: String,
|
||||
pub priority: String,
|
||||
pub created_at: i64,
|
||||
pub updated_at: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
||||
@ -57,20 +57,20 @@ impl Default for InMemoryTaskRepository {
|
||||
#[async_trait]
|
||||
impl TaskRepository for InMemoryTaskRepository {
|
||||
async fn save_task_session(&self, session: &TaskSession) -> Result<(), StorageError> {
|
||||
tracing::warn!(
|
||||
tracing::debug!(
|
||||
task_id = %session.id,
|
||||
session_id = %session.session_id,
|
||||
state = ?session.state,
|
||||
"REPO_SAVE: Saving task session"
|
||||
"Saving task session"
|
||||
);
|
||||
self.sessions
|
||||
.write()
|
||||
.unwrap()
|
||||
.insert(session.id.clone(), session.clone());
|
||||
tracing::warn!(
|
||||
tracing::debug!(
|
||||
task_id = %session.id,
|
||||
total_tasks = self.sessions.read().unwrap().len(),
|
||||
"REPO_SAVE: Task session saved, current repository size"
|
||||
"Task session saved, current repository size"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
@ -79,11 +79,11 @@ impl TaskRepository for InMemoryTaskRepository {
|
||||
let sessions = self.sessions.read().unwrap();
|
||||
let total = sessions.len();
|
||||
let keys: Vec<&str> = sessions.keys().map(|k| k.as_str()).collect();
|
||||
tracing::warn!(
|
||||
tracing::debug!(
|
||||
lookup_task_id = %task_id,
|
||||
total_tasks = total,
|
||||
all_keys = ?keys,
|
||||
"REPO_LOOKUP: Looking up task session"
|
||||
"Looking up task session"
|
||||
);
|
||||
Ok(sessions.get(task_id).cloned())
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user