/// FIST-Mbt core: task model & state machine.
/// 迁移自 FIST(Python) 的状态机与分层递归理念(七条金条第 4 条:唯一指挥官)。
///|
/// 任务七态状态机
pub enum TaskStatus {
/// 待领取
Pending
/// 已领取
Claimed
/// 拆分中
Splitting
/// 执行中
Executing
/// 待验收
Reviewing
/// 已完成
Completed
/// 已归档
Archived
/// 已打回(验收不通过,需重做)
Rejected
/// 已暂停(外部中断/等待依赖)
Paused
} derive(Eq, Debug)
///|
pub fn TaskStatus::to_string(self : TaskStatus) -> String {
match self {
Pending => "待领取"
Claimed => "已领取"
Splitting => "拆分中"
Executing => "执行中"
Reviewing => "待验收"
Completed => "已完成"
Archived => "已归档"
Rejected => "已打回"
Paused => "已暂停"
}
}
///|
pub fn TaskStatus::from_string(s : String) -> TaskStatus? {
match s {
"待领取" => Some(Pending)
"已领取" => Some(Claimed)
"拆分中" => Some(Splitting)
"执行中" => Some(Executing)
"待验收" => Some(Reviewing)
"已完成" => Some(Completed)
"已归档" => Some(Archived)
"已打回" => Some(Rejected)
"已暂停" => Some(Paused)
_ => None
}
}
/// 排序权重(用于统计/展示)
///|
/// 状态谓词(供跨包业务层判断,避免直接构造变体)
pub fn TaskStatus::can_split(self : TaskStatus) -> Bool {
match self {
Pending | Claimed | Splitting | Rejected => true
_ => false
}
}
///|
pub fn TaskStatus::can_execute(self : TaskStatus) -> Bool {
match self {
Claimed | Splitting | Executing | Rejected => true
_ => false
}
}
///|
pub fn TaskStatus::is_rejected(self : TaskStatus) -> Bool {
self == Rejected
}
///|
pub fn TaskStatus::is_paused(self : TaskStatus) -> Bool {
self == Paused
}
///|
pub fn TaskStatus::is_active(self : TaskStatus) -> Bool {
match self {
Pending | Claimed | Splitting | Executing | Reviewing => true
_ => false
}
}
///|
pub fn TaskStatus::is_completed(self : TaskStatus) -> Bool {
self == Completed
}
///|
pub fn TaskStatus::is_pending(self : TaskStatus) -> Bool {
self == Pending
}
///|
pub fn TaskStatus::is_claimed(self : TaskStatus) -> Bool {
self == Claimed
}
///|
pub fn TaskStatus::is_splitting(self : TaskStatus) -> Bool {
self == Splitting
}
///|
pub fn TaskStatus::is_executing(self : TaskStatus) -> Bool {
self == Executing
}
///|
pub fn TaskStatus::is_reviewing(self : TaskStatus) -> Bool {
self == Reviewing
}
///|
pub fn TaskStatus::is_archived(self : TaskStatus) -> Bool {
self == Archived
}
///|
pub fn TaskStatus::order(self : TaskStatus) -> Int {
match self {
Pending => 0
Claimed => 1
Splitting => 2
Executing => 3
Reviewing => 4
Completed => 5
Archived => 6
Rejected => 7
Paused => 8
}
}
///|
/// FIST-Mbt 任务实体
pub struct Task {
/// 形如 T0 / T0.1 / T0.1.2 的层级 id
id : String
parent_id : String?
project_dir : String
/// 命名空间(多租户隔离键,默认 "default")
ns : String
priority : String
importance : String
/// K 值:根=3,每拆一层减 1;K<=1 为原子任务
depth : Int
/// 拆分数量(默认 3)
split_n : Int
status : TaskStatus
assignee : String?
description : String
deliverable : String
created_at : String
updated_at : String
completed_by : String?
cleanup_mode : String
/// 依赖的任务 id 列表(这些任务完成后,本任务才可领取)
depends_on : Array[String]
} derive(Eq, Debug)
///|
/// 便捷状态访问
pub fn Task::status_to_string(self : Task) -> String {
self.status.to_string()
}
///|
pub fn Task::get_id(self : Task) -> String {
self.id
}
///|
pub fn Task::get_parent(self : Task) -> String? {
self.parent_id
}
///|
pub fn Task::get_status(self : Task) -> TaskStatus {
self.status
}
///|
pub fn Task::get_depth(self : Task) -> Int {
self.depth
}
///|
pub fn Task::get_description(self : Task) -> String {
self.description
}
///|
pub fn Task::get_assignee(self : Task) -> String? {
self.assignee
}
///|
pub fn Task::get_updated_at(self : Task) -> String {
self.updated_at
}
///|
pub fn Task::is_leaf(self : Task) -> Bool {
self.depth <= 1
}
///|
/// 构造器(字段带默认值)
pub fn Task::new(
id~ : String,
project_dir~ : String,
description? : String = "",
priority? : String = "中",
importance? : String = "中",
depth? : Int = 3,
split_n? : Int = 3,
parent_id? : String = "",
assignee? : String = "",
cleanup_mode? : String = "deferred",
created_at? : String = "",
ns? : String = "default",
depends_on? : Array[String] = [],
) -> Task {
{
id,
parent_id: if parent_id == "" {
None
} else {
Some(parent_id)
},
project_dir,
ns,
priority,
importance,
depth,
split_n,
status: Pending,
assignee: if assignee == "" {
None
} else {
Some(assignee)
},
description,
deliverable: "",
created_at,
updated_at: created_at,
completed_by: None,
cleanup_mode,
depends_on,
}
}
///|
/// 按存储行字段直接构建任务(供 store 层反序列化使用;status 为存储字符串,非法回退 Pending)
pub fn Task::from_db(
id : String,
parent_id : String?,
project_dir : String,
ns : String,
priority : String,
importance : String,
depth : Int,
split_n : Int,
status_s : String,
assignee : String?,
description : String,
deliverable : String,
created_at : String,
updated_at : String,
completed_by : String?,
cleanup_mode : String,
depends_on : Array[String],
) -> Task {
let status = match TaskStatus::from_string(status_s) {
Some(s) => s
None => Pending
}
{
id,
parent_id,
project_dir,
ns,
priority,
importance,
depth,
split_n,
status,
assignee,
description,
deliverable,
created_at,
updated_at,
completed_by,
cleanup_mode,
depends_on,
}
}
/// —— 状态迁移 ——
/// 每个迁移都是纯函数(返回新 Task),状态机非法流转直接拒绝。
/// 返回 Err(msg) 表示非法迁移。
///|
/// 待领取 -> 已领取(角色:any active agent)
pub fn Task::claim(self : Task, assignee~ : String) -> Result[Task, String] {
match self.status {
Pending =>
if assignee == "" {
Err("claim 需要 assignee")
} else {
Ok({
..self,
status: Claimed,
assignee: Some(assignee),
updated_at: self.updated_at,
})
}
_ =>
Err(
"非法迁移: claim 要求状态 [待领取],当前是 [" +
self.status.to_string() +
"]",
)
}
}
///|
/// 已领取 -> 拆分中(指挥官拆解)
pub fn Task::split(self : Task) -> Result[Task, String] {
match self.status {
Claimed =>
if self.is_leaf() {
Err("原子任务(K<=1)不可再拆分")
} else {
Ok({ ..self, status: Splitting, })
}
_ =>
Err(
"非法迁移: split 要求状态 [已领取],当前是 [" +
self.status.to_string() +
"]",
)
}
}
///|
/// 拆分中标记(AO 式拆解用,宽于 split:待领取/已领取/拆分中均可转入)
pub fn Task::mark_decomposing(self : Task) -> Result[Task, String] {
match self.status {
Pending | Claimed | Splitting => Ok({ ..self, status: Splitting, })
_ => Err("非法拆解: 任务处于 [\{self.status.to_string()}]")
}
}
///|
/// 拆分中 -> 执行中(进入执行)
pub fn Task::execute(self : Task) -> Result[Task, String] {
match self.status {
Splitting | Claimed => Ok({ ..self, status: Executing, })
_ =>
Err(
"非法迁移: execute 要求状态 [拆分中/已领取],当前是 [" +
self.status.to_string() +
"]",
)
}
}
///|
/// 执行中 -> 待验收
pub fn Task::submit(self : Task) -> Result[Task, String] {
match self.status {
Executing => Ok({ ..self, status: Reviewing, })
_ =>
Err(
"非法迁移: submit 要求状态 [执行中],当前是 [" +
self.status.to_string() +
"]",
)
}
}
///|
/// 待验收 -> 已完成(人类主权:completed_by 必须 human / human_steward 或明确授权)
pub fn Task::complete(
self : Task,
completed_by~ : String,
) -> Result[Task, String] {
match self.status {
Reviewing =>
if completed_by == "" {
Err("complete 需要 completed_by 身份")
} else {
Ok({ ..self, status: Completed, completed_by: Some(completed_by), })
}
_ =>
Err(
"非法迁移: complete 要求状态 [待验收],当前是 [" +
self.status.to_string() +
"]",
)
}
}
///|
/// 已完成 -> 已归档
pub fn Task::archive(self : Task) -> Result[Task, String] {
match self.status {
Completed => Ok({ ..self, status: Archived, })
_ =>
Err(
"非法迁移: archive 要求状态 [已完成],当前是 [" +
self.status.to_string() +
"]",
)
}
}
///|
/// 任意非归档状态 -> 已领取(被打回重做)
pub fn Task::reopen(self : Task) -> Result[Task, String] {
match self.status {
Archived => Err("已归档任务不可 reopen,请重新发布")
Rejected =>
Ok({ ..self, status: Claimed, assignee: None, deliverable: "", })
Paused => Ok({ ..self, status: Claimed, })
_ => Ok({ ..self, status: Claimed, })
}
}
///|
/// 待验收 -> 已打回(验收不通过,需重做)
pub fn Task::reject(self : Task, reason? : String = "") -> Result[Task, String] {
match self.status {
Reviewing =>
Ok({
..self,
status: Rejected,
description: if reason == "" {
self.description
} else {
self.description + " [打回原因: " + reason + "]"
},
})
_ =>
Err(
"非法迁移: reject 要求状态 [待验收],当前是 [" +
self.status.to_string() +
"]",
)
}
}
///|
/// 已打回 -> 执行中(重新执行)
pub fn Task::retry(self : Task) -> Result[Task, String] {
match self.status {
Rejected => Ok({ ..self, status: Executing, })
_ =>
Err(
"非法迁移: retry 要求状态 [已打回],当前是 [" +
self.status.to_string() +
"]",
)
}
}
///|
/// 任意活跃状态 -> 已暂停(外部中断/等待依赖)
pub fn Task::pause(self : Task) -> Result[Task, String] {
match self.status {
Pending | Claimed | Splitting | Executing | Reviewing | Rejected =>
Ok({ ..self, status: Paused, })
Paused => Err("任务已处于暂停状态")
_ =>
Err(
"非法迁移: pause 要求活跃状态,当前是 [" +
self.status.to_string() +
"]",
)
}
}
///|
/// 已暂停 -> 已领取(恢复执行)
pub fn Task::resume_task(self : Task) -> Result[Task, String] {
match self.status {
Paused => Ok({ ..self, status: Claimed, })
_ =>
Err(
"非法迁移: resume_task 要求状态 [已暂停],当前是 [" +
self.status.to_string() +
"]",
)
}
}
/// 生成子任务 id:T0.1.2 -> T0.1.2.1
///|
/// 记录类复制更新(跨包不便直接构造 record,由 core 提供)
pub fn Task::with_updated_at(self : Task, now : String) -> Task {
{ ..self, updated_at: now, }
}
///|
pub fn Task::with_deliverable(
self : Task,
deliverable : String,
now : String,
) -> Task {
{ ..self, deliverable, updated_at: now, }
}
///|
pub fn child_id(parent_id~ : String, idx~ : Int) -> String {
"\{parent_id}.\{idx}"
}
///|
/// 任务转 JSON(供 MCP resource / tool 输出)
pub fn Task::to_json(self : Task) -> Json {
let m : Map[String, Json] = Map([])
m.set("id", Json::string(self.id))
match self.parent_id {
Some(p) => m.set("parent_id", Json::string(p))
None => m.set("parent_id", Json::null())
}
m.set("project_dir", Json::string(self.project_dir))
m.set("namespace", Json::string(self.ns))
m.set("priority", Json::string(self.priority))
m.set("importance", Json::string(self.importance))
m.set("depth", Json::number(self.depth.to_double()))
m.set("split_n", Json::number(self.split_n.to_double()))
m.set("status", Json::string(self.status.to_string()))
match self.assignee {
Some(a) => m.set("assignee", Json::string(a))
None => m.set("assignee", Json::null())
}
m.set("description", Json::string(self.description))
m.set("deliverable", Json::string(self.deliverable))
m.set("created_at", Json::string(self.created_at))
m.set("updated_at", Json::string(self.updated_at))
match self.completed_by {
Some(c) => m.set("completed_by", Json::string(c))
None => m.set("completed_by", Json::null())
}
m.set("cleanup_mode", Json::string(self.cleanup_mode))
m.set("is_leaf", Json::boolean(self.is_leaf()))
Json::object(m)
}