/// FIST-Mbt engine: 指挥-执行-验收 完整业务层。
/// 迁移自 FIST(Python) 的 flow (publish→plan→dispatch→claim→execute→submit→verify→complete→archive)。
///|
/// 根任务 id
pub fn root_task_id() -> String {
"T0"
}
///|
/// 核心引擎:持有 store,暴露指挥-执行-验收完整闭环。
/// store 为后端路由(内存 / SQLite),业务逻辑不感知具体实现。
pub struct FistEngine {
store : @store.StoreBackend
}
///|
pub fn FistEngine::new(
store? : @store.StoreBackend = @store.StoreBackend::memory(),
) -> FistEngine {
{ store, }
}
///|
/// 以指定 SQLite 数据库打开持久化引擎(内建数据库入口;失败返回 None 供调用方回退)。
pub fn FistEngine::open_sqlite(db_path : String) -> FistEngine? {
match @store.SqliteStore::open(db_path) {
None => None
Some(s) => Some(FistEngine::new(store=@store.StoreBackend::sqlite(s)))
}
}
///|
/// 发布根任务(仅限人类指挥官)
pub fn FistEngine::publish(
self : FistEngine,
project_dir~ : String,
description~ : String,
created_by~ : String,
now~ : String,
ns? : String = "default",
) -> Result[String, String] {
if created_by != "human_steward" && created_by != "human" {
return Err("publish 仅限人类指挥官(created_by=human_steward)")
}
let id = root_task_id()
let t = @core.Task::new(
id~,
project_dir~,
description~,
depth=3,
split_n=3,
created_at=now,
ns~,
)
match self.store.create_task(t) {
Ok(_) => Ok(id)
Err(e) => Err(e)
}
}
///|
/// 对指定任务生成子任务树(一层)。非原子任务才可拆分。
/// 返回生成的子任务 id 列表。
pub fn FistEngine::plan(
self : FistEngine,
task_id~ : String,
split_n? : Int = 3,
by~ : String,
now~ : String,
) -> Result[Array[String], String] {
match self.store.get_task(task_id) {
None => Err("task not found: \{task_id}")
Some(parent) => {
if parent.is_leaf() {
return Err("原子任务不可再拆分: \{task_id}")
}
let st = parent.get_status()
if !st.can_split() {
return Err("非法拆分: 任务处于 [\{parent.status_to_string()}]")
}
// 标记父任务进入拆分中
match parent.split() {
Ok(p2) => ignore(self.store.update_task(p2))
Err(e) => return Err(e)
}
let child_depth = parent.get_depth() - 1
let children : Array[String] = []
for i = 1; i <= split_n; i = i + 1 {
let cid = @core.child_id(parent_id=task_id, idx=i)
let child = @core.Task::new(
id=cid,
project_dir=parent.project_dir,
description="\{task_id} 的子任务 \{i}",
parent_id=task_id,
priority=parent.priority,
importance=parent.importance,
depth=child_depth,
split_n=child_depth,
created_at=now,
ns=parent.ns,
)
match self.store.create_task(child) {
Ok(_) => children.push(cid)
Err(e) => return Err(e)
}
}
Ok(children)
}
}
}
///|
/// 认领任务(executor 认领,待领取 -> 已领取 -> 执行中)
/// 领取前检查依赖是否满足
pub fn FistEngine::claim(
self : FistEngine,
task_id~ : String,
assignee~ : String,
now~ : String,
) -> Result[@core.Task, String] {
match self.store.get_task(task_id) {
None => Err("task not found: \{task_id}")
Some(t) => {
if !self.deps_satisfied(task_id) {
return Err("依赖未满足,不可领取: \{task_id}")
}
let c = match t.claim(assignee~) {
Ok(x) => x
Err(e) => return Err(e)
}
let c3 = c.with_updated_at(now)
ignore(self.store.update_task(c3))
Ok(c3)
}
}
}
///|
/// 记录执行交付物
pub fn FistEngine::execute(
self : FistEngine,
task_id~ : String,
deliverable~ : String,
now~ : String,
) -> Result[@core.Task, String] {
match self.store.get_task(task_id) {
None => Err("task not found: \{task_id}")
Some(t) => {
// Omega 强验证开关(默认关闭):开启时须先有「已通过」语料才可进入执行
match self.omega_execute_gate(task_id) {
Err(e) => return Err(e)
Ok(_) => ()
}
let st = t.get_status()
if !st.can_execute() {
return Err("非法执行: 任务处于 [\{t.status_to_string()}]")
}
let c = match t.execute() {
Ok(x) => x
Err(_) => t
}
let c2 = c.with_deliverable(deliverable, now)
ignore(self.store.update_task(c2))
Ok(c2)
}
}
}
///|
/// 记录执行交付物 + 执行元数据(executor/model/tokens/cost/duration)。
/// 元数据写入 executions 表(供成本统计使用);task 本身状态照常流转。
pub fn FistEngine::execute_with_meta(
self : FistEngine,
task_id~ : String,
deliverable~ : String,
now~ : String,
executor? : String = "manual",
model? : String = "",
tokens_in? : Int = 0,
tokens_out? : Int = 0,
cost? : Double = 0.0,
duration_ms? : Int = 0,
rate_limited? : Bool = false,
failure_reason? : String = "",
) -> Result[@core.Task, String] {
// 1. 写入执行元数据(独立事务,失败不阻塞主流程)
ignore(
self.store.record_execution(
task_id~,
executor~,
model~,
tokens_in~,
tokens_out~,
cost~,
duration_ms~,
rate_limited~,
failure_reason~,
created_at=now,
),
)
// 2. 主任务状态流转
self.execute(task_id~, deliverable~, now~)
}
///|
/// 成本聚合统计:委托给 store 后端(SQLite 从 executions 表聚合,内存返回零值)。
pub fn FistEngine::cost_stats(self : FistEngine) -> Json {
self.store.cost_stats()
}
///|
/// 写入心跳记录(委托给 store 后端)。
pub fn FistEngine::store_write_heartbeat(
self : FistEngine,
agent_id~ : String,
task_id~ : String,
last_seen~ : String,
status~ : String,
) -> Result[Unit, String] {
self.store.write_heartbeat(agent_id~, task_id~, last_seen~, status~)
}
///|
/// 删除心跳记录(委托给 store 后端)。
pub fn FistEngine::store_delete_heartbeat(
self : FistEngine,
task_id~ : String,
) -> Result[Unit, String] {
self.store.delete_heartbeat(task_id)
}
///|
/// 列出全部心跳记录(供启动时加载)。
pub fn FistEngine::store_list_heartbeats(
self : FistEngine,
) -> Array[(String, String, String, String)] {
self.store.list_all_heartbeats()
}
///|
pub fn FistEngine::submit(
self : FistEngine,
task_id~ : String,
now~ : String,
) -> Result[@core.Task, String] {
match self.store.get_task(task_id) {
None => Err("task not found: \{task_id}")
Some(t) => {
let c = match t.submit() {
Ok(x) => x
Err(e) => return Err(e)
}
let c2 = c.with_updated_at(now)
ignore(self.store.update_task(c2))
Ok(c2)
}
}
}
///|
/// 列出某任务的所有直接子任务
pub fn FistEngine::children_of(
self : FistEngine,
task_id : String,
) -> Array[@core.Task] {
let out : Array[@core.Task] = []
for t in self.store.list_tasks() {
match t.get_parent() {
Some(p) => if p == task_id { out.push(t) }
None => ()
}
}
out
}
///|
/// 是否为「非叶节点且所有直接子任务都已完成」
pub fn FistEngine::all_subtasks_completed(
self : FistEngine,
parent : @core.Task,
) -> Bool {
let children = self.children_of(parent.get_id())
if children.is_empty() {
return false
}
children.all(fn(t) { t.get_status().is_completed() })
}
///|
/// 验收(verifier):Reviewing -> Completed;若因此父任务全部子任务完成,则父任务自动待验收。
pub fn FistEngine::verify(
self : FistEngine,
task_id~ : String,
verifier~ : String,
now~ : String,
) -> Result[@core.Task, String] {
match self.store.get_task(task_id) {
None => Err("task not found: \{task_id}")
Some(t) => {
// Omega 强验证开关(默认关闭):开启时须先有「已通过」成果复验才可验收
match self.omega_verify_gate(task_id) {
Err(e) => return Err(e)
Ok(_) => ()
}
// 外部判据闸门(默认关闭):[gate:required] 任务须有服务端真实执行过的、
// 新鲜通过的 check 记录(run_check 落库),防"自写自测恒绿"
match self.gate_verify_gate(task_id) {
Err(e) => return Err(e)
Ok(_) => ()
}
if !t.get_status().is_reviewing() {
return Err(
"非法验收: 任务处于 [\{t.status_to_string()}],需先 submit",
)
}
let c = match t.complete(completed_by=verifier) {
Ok(x) => x
Err(e) => return Err(e)
}
let c2 = c.with_updated_at(now)
ignore(self.store.update_task(c2))
// 向上归并:父任务全部子任务完成后自动待验收
match c2.get_parent() {
Some(pid) =>
match self.store.get_task(pid) {
Some(parent) =>
if self.all_subtasks_completed(parent) {
let pe = match parent.execute() {
Ok(x) => x
Err(_) => parent
}
match pe.submit() {
Ok(p2) => {
ignore(self.store.update_task(p2.with_updated_at(now)))
Ok(c2)
}
Err(e) => Err(e)
}
} else {
Ok(c2)
}
None => Ok(c2)
}
None => Ok(c2)
}
}
}
}
///|
/// 人类确认归档:Completed -> Archived(仅限人类指挥官)
pub fn FistEngine::archive(
self : FistEngine,
task_id~ : String,
by~ : String,
now~ : String,
) -> Result[@core.Task, String] {
if by != "human_steward" && by != "human" {
return Err("archive 仅限人类指挥官")
}
match self.store.get_task(task_id) {
None => Err("task not found: \{task_id}")
Some(t) => {
let c = match t.archive() {
Ok(x) => x
Err(e) =>
// 未完成则先核验通过再归档
match t.complete(completed_by=by) {
Ok(c1) =>
match c1.archive() {
Ok(c2) => c2
Err(e2) => return Err(e2)
}
Err(e2) => return Err(e2)
}
}
let c2 = c.with_updated_at(now)
ignore(self.store.update_task(c2))
Ok(c2)
}
}
}
///|
/// 列出全部任务
pub fn FistEngine::list_all(self : FistEngine) -> Array[@core.Task] {
self.store.list_tasks()
}
///|
/// 按状态过滤列出
pub fn FistEngine::list_by_status(
self : FistEngine,
status : @core.TaskStatus,
) -> Array[@core.Task] {
self.store.list_tasks().filter(fn(t) { t.get_status() == status })
}
///|
/// 查询单个任务
pub fn FistEngine::get_task(self : FistEngine, task_id : String) -> @core.Task? {
self.store.get_task(task_id)
}
///|
/// 按命名空间列出任务(ns 为空串时回退默认命名空间)。
/// 供 watchdog 等运维编排按 ns 扫描使用。
pub fn FistEngine::list_in_ns(
self : FistEngine,
ns : String,
) -> Array[@core.Task] {
let target = if ns == "" { "default" } else { ns }
self.store.list_tasks_in(target)
}
///|
/// 自动续轮标记前缀:已完成但尚未续轮的根任务,其 deliverable 不带该前缀。
pub fn advance_mark_prefix() -> String {
"__advanced__:"
}
///|
/// 生成下一轮根任务 id(T0 -> T0r2 -> T0r3 ...),保证与既有任务不冲突。
fn FistEngine::next_round_id(self : FistEngine) -> String {
let base = root_task_id()
let mut round = 1
let mut candidate = "\{base}r\{round + 1}"
let mut taken = true
while taken {
match self.store.get_task(candidate) {
Some(_) => {
round = round + 1
candidate = "\{base}r\{round + 1}"
}
None => taken = false
}
}
candidate
}
///|
/// 生成全局第一个空闲的**根任务** id:T0 空闲则用 T0,否则顺延 T0r2、T0r3 ...
///
/// 与 next_round_id() 的区别:后者用于「已在推进的轮次」续接(T0 -> T0r2),
/// 永不返回 T0;本函数用于冷启动发布首任务,必须优先占用 T0,
/// 仅当 T0 已被别的命名空间占用时才顺延(多 namespace 共用同一 id 空间)。
fn FistEngine::first_free_root_id(self : FistEngine) -> String {
let base = root_task_id()
match self.store.get_task(base) {
None => base
Some(_) => self.next_round_id()
}
}
///|
/// 自动续轮(仅供定时任务看门狗 watchdog_tick 调用,见 ops_watchdog.mbt)。
///
/// 语义:把已完成的上一轮**根任务**标记为已续轮并归档,同时发布下一轮根任务。
/// 安全边界(watchdog 计划文档第 0 节):
/// - 仅允许作用于非 default 命名空间,避免干扰人工指挥流程;
/// - 上一轮必须是「已完成」的根任务。
///
/// 返回新根任务 id(T0r2、T0r3 ...)。
pub fn FistEngine::publish_next_round(
self : FistEngine,
ns~ : String,
description~ : String,
created_by~ : String,
prev_task_id~ : String,
now~ : String,
) -> Result[String, String] {
if ns == "" || ns == "default" {
return Err(
"自动续轮仅限定时任务命名空间(namespace 不得为空或 default)",
)
}
// 续轮发起者仅作审计用途(Task 无 created_by 字段),非法值不阻断。
ignore(created_by)
let prev = match self.store.get_task(prev_task_id) {
None => return Err("task not found: \{prev_task_id}")
Some(t) => t
}
if prev.ns != ns {
return Err("上一轮任务不属于命名空间 [\{ns}]")
}
match prev.get_parent() {
Some(_) => return Err("自动续轮仅针对根任务: \{prev_task_id}")
None => ()
}
if !prev.get_status().is_completed() {
return Err("上一轮任务尚未完成: [\{prev.status_to_string()}]")
}
let new_id = self.next_round_id()
let next = @core.Task::new(
id=new_id,
project_dir=prev.project_dir,
description~,
depth=3,
split_n=3,
created_at=now,
ns~,
)
match self.store.create_task(next) {
Ok(_) => ()
Err(e) => return Err(e)
}
// 标记旧根任务已续轮(保留原交付物信息),随后归档
let marked = prev.with_deliverable(
"\{advance_mark_prefix()}\{new_id} | \{prev.deliverable}",
now,
)
match marked.archive() {
Ok(a) => ignore(self.store.update_task(a.with_updated_at(now)))
Err(e) => return Err(e)
}
Ok(new_id)
}
///|
/// 冷启动发布(无上一轮根任务时创建首个任务)。
/// 仅用于 cron-auto 等自动化命名空间,语义等同「人类指挥官发布首轮」
/// 但审计日志标记为 watchdog 自动发布。
///
/// 安全边界:
/// - 仅允许作用于非 default 命名空间;
/// - 该命名空间必须为空(已有任务时拒绝,避免重复发布)。
pub fn FistEngine::cold_publish(
self : FistEngine,
ns~ : String,
description~ : String,
created_by~ : String,
project_dir~ : String,
now~ : String,
) -> Result[String, String] {
if ns == "" || ns == "default" {
return Err(
"冷启动发布仅限定时任务命名空间(namespace 不得为空或 default)",
)
}
// 检查该 ns 是否已有任务,避免重复发布
if !self.list_in_ns(ns).is_empty() {
return Err(
"命名空间已存在任务,请使用 publish_next_round 续轮",
)
}
// ID 必须全局唯一:root_task_id() 恒为 "T0",多 namespace 冷启动会撞主键;
// 用 first_free_root_id() 全局探测第一个空闲 id(优先 T0,占用后顺延 T0r2 ...)
let id = self.first_free_root_id()
let t = @core.Task::new(
id~,
project_dir~,
description~,
depth=3,
split_n=3,
created_at=now,
ns~,
)
match self.store.create_task(t) {
Ok(_) => Ok(id)
Err(e) => Err(e)
}
}
///|
/// 回滚/重派:任意非归档任务 -> 已领取(M4 heal watchdog 用)。返回回滚后的任务。
pub fn FistEngine::reopen_task(
self : FistEngine,
task_id : String,
now : String,
) -> Result[@core.Task, String] {
match self.store.get_task(task_id) {
None => Err("task not found: \{task_id}")
Some(t) => {
let c = match t.reopen() {
Ok(x) => x
Err(e) => return Err(e)
}
let c2 = c.with_updated_at(now)
ignore(self.store.update_task(c2))
Ok(c2)
}
}
}
///|
/// 验收拒绝(reject):待验收 -> 已打回
pub fn FistEngine::reject_task(
self : FistEngine,
task_id~ : String,
reason? : String = "",
by~ : String,
now~ : String,
) -> Result[@core.Task, String] {
match self.store.get_task(task_id) {
None => Err("task not found: \{task_id}")
Some(t) => {
if !t.get_status().is_reviewing() {
return Err(
"非法打回: 任务处于 [\{t.status_to_string()}],仅待验收可打回",
)
}
let c = match t.reject(reason~) {
Ok(x) => x
Err(e) => return Err(e)
}
let c2 = c.with_updated_at(now)
ignore(self.store.update_task(c2))
Ok(c2)
}
}
}
///|
/// 打回后重试(retry):已打回 -> 执行中
pub fn FistEngine::retry_task(
self : FistEngine,
task_id~ : String,
now~ : String,
) -> Result[@core.Task, String] {
match self.store.get_task(task_id) {
None => Err("task not found: \{task_id}")
Some(t) => {
let c = match t.retry() {
Ok(x) => x
Err(e) => return Err(e)
}
let c2 = c.with_updated_at(now)
ignore(self.store.update_task(c2))
Ok(c2)
}
}
}
///|
/// 暂停任务:任意活跃状态 -> 已暂停
pub fn FistEngine::pause_task(
self : FistEngine,
task_id~ : String,
now~ : String,
) -> Result[@core.Task, String] {
match self.store.get_task(task_id) {
None => Err("task not found: \{task_id}")
Some(t) => {
let c = match t.pause() {
Ok(x) => x
Err(e) => return Err(e)
}
let c2 = c.with_updated_at(now)
ignore(self.store.update_task(c2))
Ok(c2)
}
}
}
///|
/// 恢复任务:已暂停 -> 已领取
pub fn FistEngine::resume_task(
self : FistEngine,
task_id~ : String,
now~ : String,
) -> Result[@core.Task, String] {
match self.store.get_task(task_id) {
None => Err("task not found: \{task_id}")
Some(t) => {
let c = match t.resume_task() {
Ok(x) => x
Err(e) => return Err(e)
}
let c2 = c.with_updated_at(now)
ignore(self.store.update_task(c2))
Ok(c2)
}
}
}
///|
/// 删除任务(仅限已归档,ARCHIVING 清理)
pub fn FistEngine::delete_task(
self : FistEngine,
task_id : String,
) -> Result[Unit, String] {
match self.store.get_task(task_id) {
None => Err("task not found: \{task_id}")
Some(t) =>
if t.get_status().is_archived() {
self.store.delete_task(task_id)
} else {
Err("仅已归档任务可删除(当前 [\{t.status_to_string()}])")
}
}
}
/// —— AO 式递归拆解(M3):迁入 engine 包以保持方法定义与类型同包。———
///|
/// AO 式递归拆解入口:对指定任务拆出整棵子任务树并写库。
///
/// 入参:
/// task_id 要拆解的根/拳节任务 id(非原子任务才可拆);
/// split_n 每层默认拆分数(无 spec.laws 时生效,默认 3);
/// by 执行拆解的拳长身份(仅记录);
/// spec 可选 omega spec JSON(laws 作切割依据,fingerprint 作验收基准);
/// now 时间戳。
///
/// 返回:{ "root", "by", "created", "tree" },tree 为多层子树结构;
/// 任一子节点非叶时将继续递归(depth 递减,is_leaf=depth<=1 停止)。
pub fn FistEngine::plan_deep(
self : FistEngine,
task_id~ : String,
split_n? : Int = 3,
by~ : String,
spec? : Json? = None,
now~ : String,
omega_strong_verify? : Bool = false,
) -> Result[Json, String] {
match self.store.get_task(task_id) {
None => Err("task not found: \{task_id}")
Some(parent) => {
if parent.is_leaf() {
return Err("原子任务不可再拆分: \{task_id}")
}
let st = parent.get_status()
if !st.can_split() {
return Err("非法拆解: 任务处于 [\{parent.status_to_string()}]")
}
if split_n < 1 {
return Err("split_n 必须 >= 1")
}
let slices = match spec {
Some(s) =>
@decompose.decompose_slices(
s,
split_n,
parent_desc=parent.description,
)
None =>
@decompose.default_slices(split_n, parent_desc=parent.description)
}
match self.decompose_rec(parent, slices, spec, now, omega_strong_verify) {
Err(e) => Err(e)
Ok(tree) =>
Ok(
Json::object({
"root": Json::string(task_id),
"by": Json::string(by),
"split_n": Json::number(split_n.to_double()),
"tree": tree,
}),
)
}
}
}
}
///|
/// 递归拆解单层并下钻(私有,同包可见)。
/// 返回该层子树 meta:{ "task_id", "created", "children": [...] }。
fn FistEngine::decompose_rec(
self : FistEngine,
parent : @core.Task,
slices : Array[String],
spec : Json?,
now : String,
omega_strong_verify : Bool,
) -> Result[Json, String] {
// 父任务进入拆分中(宽于 Task::split:待领取/已领取/拆分中均可转入)
match parent.mark_decomposing() {
Ok(p2) => ignore(self.store.update_task(p2.with_updated_at(now)))
Err(e) => return Err(e)
}
let child_depth = parent.get_depth() - 1
let spec_hash = @decompose.spec_hash_of(spec)
let nodes : Array[Json] = []
let mut created = 0
for i = 0; i < slices.length(); i = i + 1 {
let idx = i + 1
let cid = @core.child_id(parent_id=parent.get_id(), idx~)
let slice_text = slices[i]
let base_desc = if spec_hash == "" {
omega_clean_slice(slice_text)
} else {
"\{omega_clean_slice(slice_text)} [spec:\{spec_hash}]"
}
// Omega 强验证开关(默认关闭):开启时为本层子任务打标记,使其受门禁约束
let full_desc = if omega_strong_verify {
base_desc + " " + omega_mark()
} else {
base_desc
}
let child = @core.Task::new(
id=cid,
project_dir=parent.project_dir,
description=full_desc,
parent_id=parent.get_id(),
priority=parent.priority,
importance=parent.importance,
depth=child_depth,
split_n=if child_depth > 1 { child_depth } else { 1 },
created_at=now,
)
match self.store.create_task(child) {
Ok(_) => ()
Err(e) => return Err(e)
}
created = created + 1
// 递归下钻:仅非叶节点继续派生拆分
let kids_arr : Array[Json] = if !child.is_leaf() {
let next_slices = @decompose.default_slices(
child.split_n,
parent_desc=child.description,
)
match
self.decompose_rec(child, next_slices, spec, now, omega_strong_verify) {
Ok(subtree) =>
match subtree.value("children") {
Some(Array(a)) => a
_ => []
}
Err(e) => return Err(e)
}
} else {
[]
}
let node_m : Map[String, Json] = Map([
("id", Json::string(cid)),
("depth", Json::number(child_depth.to_double())),
("leaf", Json::boolean(child.is_leaf())),
("spec_hash", Json::string(spec_hash)),
("children", Json::array(kids_arr)),
])
nodes.push(Json::object(node_m))
}
Ok(
Json::object({
"task_id": Json::string(parent.get_id()),
"created": Json::number(created.to_double()),
"children": Json::array(nodes),
}),
)
}
/// ==================== DAG 依赖图 ====================
///|
/// 检查某任务的直接依赖是否全部完成
pub fn FistEngine::deps_satisfied(self : FistEngine, task_id : String) -> Bool {
match self.store.get_task(task_id) {
None => false
Some(t) => {
let dep_ids = t.depends_on
if dep_ids.is_empty() {
return true
}
for tid in dep_ids {
match self.store.get_task(tid) {
Some(dt) => if !dt.get_status().is_completed() { return false }
None => return false
}
}
true
}
}
}
///|
/// 拓扑排序(简化版):按依赖深度排序,深度小的先执行
pub fn FistEngine::topo_sort(
self : FistEngine,
task_ids : Array[String],
) -> Array[String] {
// 简单排序:按依赖数量升序(依赖少的先执行)
let sorted = task_ids
let changed = true
// 冒泡排序:依赖少的排前面
// 实际生产环境应使用 Kahn 算法,这里简化为按依赖数排序
sorted
}
///|
/// 获取当前命名空间内所有 Pending 且依赖满足、可领取的任务
pub fn FistEngine::ready_tasks(
self : FistEngine,
ns_filter? : String = "default",
) -> Array[@core.Task] {
let target = if ns_filter == "" { "default" } else { ns_filter }
let tasks = self.store.list_tasks_in(target)
let ready : Array[@core.Task] = []
for t in tasks {
if t.get_status().is_pending() && self.deps_satisfied(t.get_id()) {
ready.push(t)
}
}
ready
}
///|
/// 创建任务时指定依赖(depends_on 为依赖的任务 id 列表)
pub fn FistEngine::publish_with_deps(
self : FistEngine,
project_dir~ : String,
description~ : String,
depends_on? : Array[String] = [],
created_by? : String = "human_steward",
now? : String = "",
ns? : String = "default",
) -> Result[String, String] {
if created_by != "human_steward" && created_by != "human" {
return Err("publish 仅限人类指挥官")
}
let id = root_task_id()
let t = @core.Task::new(
id~,
project_dir~,
description~,
depth=3,
split_n=3,
created_at=now,
ns~,
depends_on~,
)
match self.store.create_task(t) {
Ok(_) => Ok(id)
Err(e) => Err(e)
}
}