待办清单

文档整理、待拍板的决策、PR #519 合并前要做的事,以及后续技术路线。勾选状态只保存在当前浏览器里。

建议顺序

  1. 最紧急:PR #519 合并前的 12–18。一旦合并发布,改公开 API 就是破坏性变更。
  2. 最关键:决策 8a、8b、9(以及 10)。不定下来,审批层原型(阶段 3)没法开工。8b 先于 8a。建议写成一份决策记录。
  3. 随时可做:文档收尾 5、6,不挡别的事。

一、文档整理

把 pages 上这套文档和 PR #542 的现状对齐,补齐缺的材料。

1

修正和 PR #542 对不上的数字和代码位置

用例数改成 328 / 50 limitation,转换数 19 改成 20,"验证代码未提交"改为指向 PR #542,总览页的示例数。

已完成
2

两份示例文档合成一份

合并成 improvement-plan.md:16 个示例分 6 组,路线图保留 20 项、4 个阶段。

已完成
3

补原生实验报告 native-contract-evaluation.md

原文已放进来:native-contract-evaluation.md,开头加了现状注记(limitation 用例原文写 4 个,PR 里是 3 个)。

已完成
4

补架构设计 approval-architecture.md

原文已放进来:approval-architecture.md,开头注记了和后续文档不一致的地方(AI 协同、外层环节、流程即代码、API 路径风格)。

已完成
5

在 PR #542 上实际跑一遍测试

确认用例是否"全部通过",以及"插件 tsc、ESLint 通过",再据此更新 summary.md 2.2 节的措辞。约十几分钟。

待做
6

pr-519-lifecycle-vs-forge.md 开头加视角说明

它是站在 Forge(app-plugin-projects)角度写的,文中的"我们的 workflows"指 Forge 的流程定义,不是 workflow 插件。

小
7

把改动前的备份挪到持久位置

pages 已放进私有仓库 chenos/pages:第一个提交是改动前的版本,第二个提交是这次整理,用 git diff HEAD~1 就能对比。Caddy 同时关掉了目录列表并隐藏 .git。

已完成

二、待拍板的决策

8a–10 可以写成一份决策记录:列出选项、依据和建议,讨论后定案。8a 的方案依赖 8b。11 要等产品定义。

8a

流程定义的版本怎么管理

要定的:① 范围:只有审批要管版本,还是所有 lifecycle 都要管。approval-architecture.md 第十一节第 7 点说业务记录的 lifecycle 有意不做版本;abstraction-notes.md 3.1 说凡是持久的业务流程都需要;lifecycle 库目前完全没有版本概念。② 在途实例:默认按提交时的版本走完,还是允许、何时允许迁移到新版本。③ 方案:版本从哪里来、怎么判断"必须升版"、旧版本保留到什么时候。

现有方案:summary.md 第八节和 approval-architecture.md 第十一节已有完整设计(在途固定版本、结构指纹 + 锁文件强制升版、找不到版本拒绝启动、显式迁移),但建立在"流程即代码"上,8b 的结论可能推翻它。

依赖:8b 定了,③ 才能定;① 和 ② 可以先讨论。阶段 2 的"同表多定义"和阶段 3 的"版本指纹与显式迁移"都等它。

关键待拍板依赖 8bimprovement-plan 示例 15
8b

流程定义要不要支持在 runtime 修改

要定的:① 哪些场景确实需要 runtime 修改:Forge 的流程定义存在数据库里、按项目分配、运行时可编辑;V3 的 workflow 也预期能在 runtime 更新。② 如果支持,和代码定义的流程是共用一个 lifecycle 内核,还是分成两套机制。

影响:8a 的版本方案(锁文件指纹、CI 检查、找不到版本拒绝启动)要不要重做;lifecycle 能不能同时承载 Forge,Forge 的评估把"定义即数据、按记录解析定义"列为第一条必须项;也和 #519 的内核 API 有关(定义注册目前同名不可替换)。

关键待拍板先于 8aabstraction-notesPR #519 vs Forge
9

外层环节用生成的真实状态,还是只存数据的计划

影响:审批层原型怎么做。前者保留按环节计时和准确的按钮可用性,后者加签和升版更简单。PR #542 的现状就是后者,可以直接和前者在同一组场景上对比。

10

和 workflow 插件、AI employee 人机中断的关系

影响:任务模型要不要统一这几种"等人处理"的机制。现有的是 workflow 的节点挂起(resumeNode)和 AI employee 的 AgentInvokeInterrupt / resumeInvoke。需要先读这两个插件的实现。approval-architecture.md 第六节已经设计了"人与 AI 协同"走任务内核,可以作为起点。

待拍板需先调研
11

统一待办是否覆盖审批以外的人工任务;人与 AI 协同用什么交互

影响:任务内核的范围。定义之前,任务内核的接口只保证不依赖审批特有的概念。

产品问题

三、PR #519 合并前

12–15 会改变公开 API 的语义,发布后再改就是破坏性变更。可以整理成一份清单和 #519 的作者对。

12

create() 走守卫和校验

现在 create() 只检查初始状态,谁能创建只能在路由里管(缺口 9)。

已完成:定义新增 create: { validate, guard },runtime.create() 在事务内先校验值、再问守卫,拒绝格式与转换相同(INVALID_INPUT / GUARD_REJECTED)。示例插件的“只有客户/员工本人能建单”和字段检查移进了定义。PR #519 提交 d2fd0ef9。

已完成合并前改 API 语义
13

请求号重放时比对转换名

现在同一个 requestId 用在不同转换上会被当成重放,吞掉一次真实操作(缺口 8)。

已完成:重放时比对转换名:同一 requestId 用在另一个转换上时抛 REQUEST_REUSED(HTTP 409),错误信息写明它已用于哪个转换,不改任何数据。文档补了错误表和排障条目。PR #519 提交 4276e599。

已完成合并前改 API 语义
14

can() 带输入,校验先于守卫

现在 available() / can() 以空输入调用守卫,按输入区分的操作拿不到准确的可用性(缺口 10)。

已完成:fire() / planTransition() 改为先 validate 再问守卫;runtime.can(name, id, transition, actor, { input }) 带输入时先校验,返回 { allowed, blockers, problems },不抛错。available() 保持不带输入,文档说明按输入区分的操作要用 can() 逐项问。PR #519 提交 ed551ab8。

已完成合并前改 API 语义
15

effect 结构化失败;限制已续接 run 的 retryRun

现在 onFailure 只拿到 { error: message };运维重试已续接的 run 可能"钱付了、账没记"(缺口 6、7)。

已完成:新增 EffectFailure(code, message, { details, retry }):默认不重试,onFailure 收到 { error, errorCode, details }。续接转换的日志以 $run:<runId>:<outcome> 作请求号,一个 run 每种结果最多续接一次;retryRun() 遇到 onFailure 已续接的 run 抛 RUN_SETTLED(409),{ force: true, reason } 才执行,重试路由和 React 客户端都能传。文档强调 idempotencyKey 只覆盖同一个 run。PR #519 提交 32f8f928。

已完成合并前改 API 语义
16

fire / create 加入调用方事务

审批评估和 Forge 评估都列为第一优先级(缺口 1)。至少在合并前把接口定下来,effect 借 PR #522 的 afterCommit 在最外层提交后派发。

已完成:fire() / create() 新增 transaction:传 onTransition 的 transactionHandle 或调用方 @nocobase/db 事务的连接,就嵌套在调用方事务里执行(db 上是 savepoint,内存 store 上是嵌套撤销日志)。拒绝只回滚嵌套部分并抛给调用方。effect 派发和监听通过新增的 LifecycleStore.afterCommit 在最外层提交后执行,回滚则丢弃。已在 SQLite 上验证 savepoint 语义。PR #519 提交 e510ba70。

已完成合并前两方共识
17

hono.ts 路由改成符合仓库 HTTP API 规范

现在路径是 kebab-case,没用 describeRoute / apiValidator,错误体手写,成功响应没包 { data }。这个包要发布,规范是硬性要求。

已完成:库删掉了 @nocobase/lifecycle/hono(路由归插件),主入口新增 lifecycleErrorFields()(拒绝 → 标准错误体字段,供插件 new ApiError({ ...fields, domain }))和 lifecycleDescriptionView()。/react 客户端改读 { data } 和标准错误体。lifecycle-example 路由改到 /api/lifecycleExample,office-flows 改到 /api/officeFlowsExample,全部用 describeRoute / apiValidator,列表分页、id 为字符串;examples 模板的 openapi:check 已通过。PR #519 提交 e6cbb294、8947ca21。

已完成合并前
18

修内存 store 的回滚,派发尊重 runAfter

回滚现在恢复整库快照,会抹掉事务外的写入;进程内派发忽略退避时间(缺口 11)。不修的话,并发测试的结论不可信。

已完成:内存 store 的事务改为撤销日志,回滚只撤销本事务的写入;@nocobase/lifecycle/testing 新增 InProcessDispatcher,按假时钟等待 runAfter,runDue() 执行到期重试,retries: 'immediate' 保留立即重试。PR #519 提交 e0157809。

已完成合并前

四、后续技术路线

来自 improvement-plan.md 的改进点汇总。阶段 1 的小修复已并入上面的 12–15、18。

阶段 2

库:审批层的前置条件

内部转换、进入/离开写字段钩子、按字段计时和触发器过滤、{ to, values } 异步钩子、冻结字段、只画可达的边、同表多定义。

前提:16 定下来;同表多定义还要等 8a
阶段 3

审批层原型

任务表和任务小状态机、策略库、defineApproval、待办 API、版本指纹与显式迁移。

前提:8a、8b、9 拍板
阶段 4

验证

用审批层重写 772 行的原生合同审批和代表场景,对比代码量;在 PostgreSQL / MySQL 上验证唯一索引、"先锁父行"和额度扣减。

前提:阶段 3 完成