vicTop-cw/fist-mbt/src/core does not have a README file

    Action

    pub enum Action {
    CreateSpec
    ReviewSpec
    } derive(Eq,
    Debug
    )

    每个任务的完整操作清单(仅保留 Omega 强验证相关 Action)

    Action::equal

    fn Action::equal(Action, Action) -> Bool

    Action::not_equal

    fn Action::not_equal(x : Action, y : Action) -> Bool

    Action::to_repr

    Action::to_string

    fn Action::to_string(self : Action) -> String

    Action 可读名(供错误消息)

    Principle

    pub struct Principle {
    index : Int
    title : String
    body : String
    } derive(Eq,
    Debug
    )

    单条原则

    Principle::equal

    fn Principle::equal(Principle, Principle) -> Bool

    Principle::get_body

    fn Principle::get_body(self : Principle) -> String

    Principle::get_index

    fn Principle::get_index(self : Principle) -> Int

    Principle::get_title

    fn Principle::get_title(self : Principle) -> String

    Principle::not_equal

    fn Principle::not_equal(x : Principle, y : Principle) -> Bool

    Principle::to_json

    fn Principle::to_json(self : Principle) -> Json

    单条原则转 JSON(供 resource 输出)

    Role

    pub enum Role {
    HumanSteward
    Leader
    Searcher
    Allocator
    Executor
    Verifier
    Recorder
    Observer
    SpecAuthor
    } derive(Eq,
    Debug
    )

    八角色

    Role::description

    fn Role::description(self : Role) -> String

    角色职责说明

    Role::equal

    fn Role::equal(Role, Role) -> Bool

    Role::from_string

    fn Role::from_string(s : String) -> Role?

    Role::not_equal

    fn Role::not_equal(x : Role, y : Role) -> Bool

    Role::to_repr

    Role::to_string

    fn Role::to_string(self : Role) -> String

    Task

    pub struct Task {
    id : String
    parent_id : String?
    project_dir : String
    ns : String
    priority : String
    importance : String
    depth : Int
    split_n : Int
    level : Int
    status : TaskStatus
    assignee : String?
    description : String
    deliverable : String
    created_at : String
    updated_at : String
    completed_by : String?
    cleanup_mode : String
    depends_on : Array[String]
    omega_required : Bool
    created_by : String
    } derive(Eq,
    Debug
    )

    FIST-Mbt 任务实体

    Task::archive

    fn Task::archive(self : Task) -> Result[Task, String]

    已完成 -> 已归档

    Task::archive_force

    fn Task::archive_force(self : Task, force~ : Bool) -> Result[Task, String]

    强制归档(BUG-227):force=true 时放行 已完成/已暂停/已打回 三类滞留态直接入档, 供运维清扫无主积压任务;force=false 时与 Task::archive 完全同义(零回归)。 已领取/执行中/待验收 等在流状态不放行——它们可能仍被活跃 agent 持有, 强制归档会吞掉在途工作;请先 pause 落到已暂停再走本路径。

    Task::claim

    fn Task::claim(self : Task, assignee~ : String) -> Result[Task, String]

    待领取 -> 已领取(角色:any active agent)

    Task::complete

    fn Task::complete(self : Task, completed_by~ : String) -> Result[Task, String]

    待验收 -> 已完成(人类主权:completed_by 必须 human / human_steward 或明确授权) 注意:此处仅校验 completed_by 非空;不引入独立的强身份认证模型(鉴权属上层 MCP 工具/MCP 层职责,本域不做权宜式身份伪造防护,避免脆弱的新鉴权模型)。

    Task::equal

    fn Task::equal(Task, Task) -> Bool

    Task::execute

    fn Task::execute(self : Task) -> Result[Task, String]

    拆分中/已领取/执行中 -> 执行中(进入/覆盖/追加交付物) 执行中 -> execute = 覆盖/追加交付物(retry 后原地更新交付物的直觉行为) BUG-2(Round 2 收口):合法前态含「执行中」。retry 的落点就是执行中,若这里不收, 「被打回重做」这条最该更新交付物的路径会写不进 deliverable(omega_result_verify 随即报「尚无交付物」),诱导操作者新建任务覆盖旧账、破坏 append-only 审计。 选择放宽 execute 而不改 retry 的落点:retry→执行中 是对外承诺的语义 (AGENTS.md 工具表「打回后重试(→执行中)」、已发布流水线的状态判据都按它写), 改它等于改一条公共状态机迁移;而「执行中重复 execute」本就是幂等追加,影响面更小。 错误文案必须给出路(当前状态 → 该走哪条迁移),否则调用方只能猜或另起任务。

    Task::execute_exit_hint

    fn Task::execute_exit_hint(status : TaskStatus) -> String

    execute 被拒时按当前状态给出可用出路(BUG-2 文案,纯函数便于回归测试)。

    Task::from_db

    fn Task::from_db(id : String, parent_id : String?, project_dir : String, ns : String, priority : String, importance : String, depth : Int, split_n : Int, level : 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], omega_required : Bool) -> Task

    按存储行字段直接构建任务(供 store 层反序列化使用;status 为存储字符串,非法回退 Pending)

    Task::from_db_with_created_by

    fn Task::from_db_with_created_by(id : String, parent_id : String?, project_dir : String, ns : String, priority : String, importance : String, depth : Int, split_n : Int, level : 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], omega_required : Bool, created_by : String) -> Task

    BUG-475:带 created_by 的行反序列化(20 参)。

    为什么另开一个而不是给 from_db 加参:from_db 是位置参数函数 (MoonBit 位置参数不能有默认值),全仓 4 个测试文件直接调它。 加第 20 个必填参会让它们全部编译失败 —— 那是用测试的改动去换一个字段, 代价不对等。⇒ 保留旧签名(委托到本函数、created_by=""), store 层改用本函数。

    Task::get_assignee

    fn Task::get_assignee(self : Task) -> String?

    Task::get_depth

    fn Task::get_depth(self : Task) -> Int

    Task::get_description

    fn Task::get_description(self : Task) -> String

    Task::get_id

    fn Task::get_id(self : Task) -> String

    Task::get_level

    fn Task::get_level(self : Task) -> Int

    军衔等级(0..7)。军衔体系展示层读取此处,而非 depth。

    Task::get_omega_required

    fn Task::get_omega_required(self : Task) -> Bool

    BUG-389 方向②:Omega 门禁开关元数据(description 被覆盖后仍为真)

    Task::get_parent

    fn Task::get_parent(self : Task) -> String?

    Task::get_status

    fn Task::get_status(self : Task) -> TaskStatus

    Task::get_updated_at

    fn Task::get_updated_at(self : Task) -> String

    Task::is_leaf

    fn Task::is_leaf(self : Task) -> Bool

    Task::mark_decomposing

    fn Task::mark_decomposing(self : Task) -> Result[Task, String]

    拆分中标记(AO 式拆解用,宽于 split:待领取/已领取/拆分中均可转入)

    Task::new

    fn Task::new(id~ : String, project_dir~ : String, description? : String, priority? : String, importance? : String, depth? : Int, split_n? : Int, level? : Int, parent_id? : String, assignee? : String, cleanup_mode? : String, created_at? : String, ns? : String, depends_on? : Array[String], omega_required? : Bool, created_by? : String) -> Task

    构造器(字段带默认值)

    Task::not_equal

    fn Task::not_equal(x : Task, y : Task) -> Bool

    Task::pause

    fn Task::pause(self : Task) -> Result[Task, String]

    任意活跃状态 -> 已暂停(外部中断/等待依赖)

    Task::reject

    fn Task::reject(self : Task, reason? : String) -> Result[Task, String]

    待验收 -> 已打回(验收不通过,需重做)

    BUG-427:打回原因不再无上限追加进 description。

    旧实现每次 reject 都拼一段 [打回原因: …],而: · 存储面无任何长度闸(desc_clip 只在 to_json 输出时截断); · description 是三处功能的共同进料面: task_triage 的能力抽取(cap_match 的 desc.contains(want))、 goal_drift_check 的 Jaccard(@evolve.tokens(get_description()))、 supervisor_status 的 200 字节截断。

    实测:T0r636.4.2 单次打回即 4737 字节。

    现实现两道闸: ① 先剥掉已有的 [打回原因: …] 尾巴(滚动窗口 = 只留最近 1 次) ② 单条原因截断到 REJECT_REASON_CAP 字节(按 UTF-8 字符边界,不切半字)

    只留最近 1 次是有意取舍:打回原因属于过程信息,留住「最近为什么被打回」 已经够驱动重做;留全部会让任务意图被历史噪声淹没。 需要完整历史时应走独立历史表(另立,动 schema)。

    Task::reopen

    fn Task::reopen(self : Task) -> Result[Task, String]

    任意非归档状态 -> 已领取(被打回重做)

    Task::resume_task

    fn Task::resume_task(self : Task) -> Result[Task, String]

    已暂停 -> 已领取(恢复执行)

    Task::retry

    fn Task::retry(self : Task) -> Result[Task, String]

    已打回 -> 执行中(重新执行)

    BUG-426:必须清空 deliverable。

    缺陷原形:reject 只改 status + 追加打回原因、retry 只改 status, 两者都不动 deliverable ⇒ execute(A) → submit → reject → retry → submit 走完一圈后,deliverable 还是被打回的那份 A,而 submit 也不查新鲜度, 于是旧废品被当成本轮成果提交验收。

    这不是设计选择,是遗漏 —— 同一条 Rejected 分支上,兄弟方法 Task::reopen(:549)清了 deliverable: "" + assignee: None。

    危害链:omega_strong.mbt:642 用 deliverable.trim() == "" 判「尚无交付物」 ⇒ 旧废品被当成果复验,判绿即真了账;board_ascii.mbt:252 的假绿档因此系统性低估。

    只清 deliverable、保留 assignee:retry 是「同一个人重做」, 而 reopen 是「重新回到可认领池」(所以那边把 assignee 也清了)。

    Task::split

    fn Task::split(self : Task) -> Result[Task, String]

    已领取 -> 拆分中(指挥官拆解)

    Task::status_to_string

    fn Task::status_to_string(self : Task) -> String

    便捷状态访问

    Task::submit

    fn Task::submit(self : Task) -> Result[Task, String]

    执行中 -> 待验收

    Task::to_json

    fn Task::to_json(self : Task, desc_limit? : Int) -> Json

    任务 JSON。desc_limit > 0 时 description 被截断到该字节上限(字符边界安全), 供查询/管家类工具按需控体积——否则膨胀的任务描述(gradient/父计划/边界四问等元信息 递归继承后动辄数 KB)会把 MCP 返回体积炸掉。默认 -1 = 不截断(零回归)。

    Task::to_repr

    Task::with_deliverable

    fn Task::with_deliverable(self : Task, deliverable : String, now : String) -> Task

    Task::with_depends_on

    fn Task::with_depends_on(self : Task, dep_id : String, now : String) -> Task

    追加一条前置依赖(去重)并更新时间戳;返回带新 depends_on 的副本。 供显式 DAG 构建(dag_depend)使用——不只靠 plan_deep 的隐式父子关系。

    Task::with_description

    fn Task::with_description(self : Task, description : String, now : String) -> Task

    追加(覆盖)任务描述并更新时间戳;返回带新 description 的副本。 供超级管家 supervisor_task_upsert 使用——只改文本,不动状态机。

    Task::with_omega_required

    fn Task::with_omega_required(self : Task, omega_required : Bool, now : String) -> Task

    设置 Omega 强验证元数据列(omega_required),并更新时间戳。 与 with_description 的文本标记路径互为补集:本方法只动元数据面, 不改描述文本 —— 两者由 omega_enabled() 的 || 合并为单一判据(判例-16:不要引第 5 种判法)。

    为什么要有:BUG-389 方向② 把门禁判据搬进元数据列之后,描述被 supervisor_task_upsert 覆盖也不会丢开关;但没有对应的写入入口。 F3 继承锁(omega_required_column_wbtest.mbt 的 wb1)必须把「父任务已开着 Omega」 这条前置真的落库——用 with_description 塞文本标记等于绕开元数据面,锁就测不到单真源。

    它不是产品面的「关闭入口」:引擎侧写入点仍只有 decompose_rec 那一处 (单调 OR,见 engine.mbt 的 F2 留痕);显式关闭语义属 advance 面,不在本单。

    Task::with_priority

    fn Task::with_priority(self : Task, priority : String, now : String) -> Task

    覆盖任务优先级(易/中/难一类的中文档位文本)并更新时间戳。

    Task::with_updated_at

    fn Task::with_updated_at(self : Task, now : String) -> Task

    记录类复制更新(跨包不便直接构造 record,由 core 提供)

    TaskStatus

    pub enum TaskStatus {
    Pending
    Claimed
    Splitting
    Executing
    Reviewing
    Completed
    Archived
    Rejected
    Paused
    } derive(Eq,
    Debug
    )

    任务七态状态机

    TaskStatus::can_execute

    fn TaskStatus::can_execute(self : TaskStatus) -> Bool

    execute 的前态谓词(engine.execute 的第一道门)。 BUG-2 相邻加固(守卫一致性):这里曾多出一条 Rejected,而 Task::execute 并不接受 已打回——两条门不一致时,调用方拿到的是引擎的"非法执行: 任务处于 [已打回]"(无出路), 而不是域迁移里带 retry 出路的文案。集合与 Task::execute 逐字对齐(对外结果不变: 两条门对 Rejected 都是拒,只是文案统一)。

    TaskStatus::can_split

    fn TaskStatus::can_split(self : TaskStatus) -> Bool

    状态谓词(供跨包业务层判断,避免直接构造变体)

    这个闸门比域方法宽是「故意的」——R142 曾误删 Rejected,R143 已回滚并写清缘由。

    三层分工: · Task::split()(engine.split 用)只收 Claimed; · Task::mark_decomposing()(engine.plan_deep 用)收 Pending | Claimed | Splitting; · 本闸门 = "值得尝试"的宽集,含 Rejected —— 因为 engine 的第二道门拒绝时会拼 split_remedy(...) 给出出路文案(见 engine.mbt 的 split_remedy docstring: 「第一道门 can_split 比它宽……只报'要求状态'不报'怎么到达',调用方就会照文案去 claim 一个已打回的任务,永远走不通」)。

    ⇒ 若把 Rejected 从闸门删掉(R142 干过):调用方只会拿到「非法拆分: [已打回]」, 丢掉「先 retry」这条唯一的出路提示 —— 那是 UX 倒退,不是清理死代码。 判据:宽闸门 + 窄域层 + 带出路文案的第二道门错误 = 三层各司其职,不是不一致。

    TaskStatus::equal

    fn TaskStatus::equal(TaskStatus, TaskStatus) -> Bool

    TaskStatus::from_string

    fn TaskStatus::from_string(s : String) -> TaskStatus?

    TaskStatus::is_active

    fn TaskStatus::is_active(self : TaskStatus) -> Bool

    TaskStatus::is_archived

    fn TaskStatus::is_archived(self : TaskStatus) -> Bool

    TaskStatus::is_claimed

    fn TaskStatus::is_claimed(self : TaskStatus) -> Bool

    TaskStatus::is_completed

    fn TaskStatus::is_completed(self : TaskStatus) -> Bool

    TaskStatus::is_executing

    fn TaskStatus::is_executing(self : TaskStatus) -> Bool

    TaskStatus::is_paused

    fn TaskStatus::is_paused(self : TaskStatus) -> Bool

    TaskStatus::is_pending

    fn TaskStatus::is_pending(self : TaskStatus) -> Bool

    TaskStatus::is_rejected

    fn TaskStatus::is_rejected(self : TaskStatus) -> Bool

    TaskStatus::is_reviewing

    fn TaskStatus::is_reviewing(self : TaskStatus) -> Bool

    TaskStatus::is_splitting

    fn TaskStatus::is_splitting(self : TaskStatus) -> Bool

    TaskStatus::not_equal

    fn TaskStatus::not_equal(x : TaskStatus, y : TaskStatus) -> Bool

    TaskStatus::order

    fn TaskStatus::order(self : TaskStatus) -> Int

    TaskStatus::to_string

    fn TaskStatus::to_string(self : TaskStatus) -> String

    TEST_ED25519_SEED_A

    let TEST_ED25519_SEED_A : Bytes

    测试用 Ed25519 种子 A(32 字节 = @mooncry 的 canonical 长度)。

    值为 0x3a 0x7f 0x21 0xb4 起头的固定序列,确定性可复现、与生产无关。 ⚠️ 仅供测试:生产私钥必须由调用方自持且随机生成,绝不用这个常量。

    TEST_ED25519_SEED_B

    let TEST_ED25519_SEED_B : Bytes

    测试用 Ed25519 种子 B(32 字节)。

    与 A 不同的一对,用于「换钥 / 错签名 / 另一主体」这类需要两把钥匙的格。

    can

    fn can(role : Role, action : Action) -> Bool

    权限判定:Can(HumanSteward, 任意) = true; 非人类角色只能做自己职责内的非破坏性动作。

    child_id

    fn child_id(parent_id~ : String, idx~ : Int) -> String

    child_level

    fn child_level(parent : Int) -> Int

    子任务军衔 = 父级 + 1,封顶排长(7)。 连长(6)→排长(7)→士兵;不再上升。父级越界先钳制再 +1(防御)。

    clip_utf8

    fn clip_utf8(s : String, cap : Int) -> String

    按 UTF-8 字符边界截断到 cap 字节(不产生半个字符)。

    与既有 desc_clip(:700 附近)同族,但语义是「纯截断」不追加 "…" —— 外层会再拼 ],加省略号反而破坏 [打回原因: …] 的形状。

    依赖面:src/core/moon.pkg:3 是 "moonbitlang/x/encoding"(不是 core/encoding/utf8), 故此处用 @encoding.encode(UTF8, s) / @encoding.decode_to(...), 与 desc_clip 同一套 API,不新增依赖。 R152:提升为 pub —— 全仓唯一的 UTF-8 边界安全截断真源。 此前它是私有、只服务「拒绝理由」一处,而 board_ascii / ops_selfdrive 里对 中文 description 直接用 desc[0:40] / desc[0:200] 硬切 ⇒ 可能切在字符中间(半个汉字)。 需要裁剪的地方一律走本函数,不要自己写 [0:N]。

    ensure_can

    fn ensure_can(role : Role, action : Action) -> Result[Unit, String]

    权限校验函数(带可读错误消息)

    ensure_can_create_spec

    fn ensure_can_create_spec(actor : String) -> Result[Unit, String]

    Omega 强验证:语料创建权校验(供 engine 包调用,避免跨包构造枚举值)

    ensure_can_review_spec

    fn ensure_can_review_spec(actor : String) -> Result[Unit, String]

    Omega 强验证:语料审核 / 成果复验权校验

    principles

    fn principles() -> Array[Principle]

    七条金条的权威定义(与 FIST-SKILL/Python 版保持同源)

    principles_json

    fn principles_json() -> Json

    全部原则转 JSON 数组

    role_of

    fn role_of(s : String) -> Result[Role, String]

    从字符串解析角色(MCP 参数用)

    test_ed25519_pub_hex

    fn test_ed25519_pub_hex(seed : Bytes) -> String

    从种子导出公钥 hex(64 字符)。

    测试里登记公钥/比对指纹一律走它,不要各自 bytes_to_hex(ed25519_public_key(...))。

    test_ed25519_sign_hex

    fn test_ed25519_sign_hex(seed : Bytes, msg : String) -> String

    用种子对消息签名,返回 hex(128 字符)。

    ⚠️ 本函数只用于测试夹具 —— 生产绝无代签能力 (服务端代签 = 自己给自己发通行证 = 绕过 S3)。