待办清单
文档整理、待拍板的决策、PR #519 合并前要做的事,以及后续技术路线。勾选状态只保存在当前浏览器里。
建议顺序
- 最紧急:PR #519 合并前的 12–18。一旦合并发布,改公开 API 就是破坏性变更。
- 最关键:决策 8a、8b、9(以及 10)。不定下来,审批层原型(阶段 3)没法开工。8b 先于 8a。建议写成一份决策记录。
- 随时可做:文档收尾 5、6,不挡别的事。
一、文档整理
把 pages 上这套文档和 PR #542 的现状对齐,补齐缺的材料。
修正和 PR #542 对不上的数字和代码位置
用例数改成 328 / 50 limitation,转换数 19 改成 20,"验证代码未提交"改为指向 PR #542,总览页的示例数。
两份示例文档合成一份
合并成 improvement-plan.md:16 个示例分 6 组,路线图保留 20 项、4 个阶段。
补原生实验报告 native-contract-evaluation.md
原文已放进来:native-contract-evaluation.md,开头加了现状注记(limitation 用例原文写 4 个,PR 里是 3 个)。
补架构设计 approval-architecture.md
原文已放进来:approval-architecture.md,开头注记了和后续文档不一致的地方(AI 协同、外层环节、流程即代码、API 路径风格)。
在 PR #542 上实际跑一遍测试
确认用例是否"全部通过",以及"插件 tsc、ESLint 通过",再据此更新 summary.md 2.2 节的措辞。约十几分钟。
pr-519-lifecycle-vs-forge.md 开头加视角说明
它是站在 Forge(app-plugin-projects)角度写的,文中的"我们的 workflows"指 Forge 的流程定义,不是 workflow 插件。
把改动前的备份挪到持久位置
pages 已放进私有仓库 chenos/pages:第一个提交是改动前的版本,第二个提交是这次整理,用 git diff HEAD~1 就能对比。Caddy 同时关掉了目录列表并隐藏 .git。
二、待拍板的决策
8a–10 可以写成一份决策记录:列出选项、依据和建议,讨论后定案。8a 的方案依赖 8b。11 要等产品定义。
流程定义的版本怎么管理
要定的:① 范围:只有审批要管版本,还是所有 lifecycle 都要管。approval-architecture.md 第十一节第 7 点说业务记录的 lifecycle 有意不做版本;abstraction-notes.md 3.1 说凡是持久的业务流程都需要;lifecycle 库目前完全没有版本概念。② 在途实例:默认按提交时的版本走完,还是允许、何时允许迁移到新版本。③ 方案:版本从哪里来、怎么判断"必须升版"、旧版本保留到什么时候。
现有方案:summary.md 第八节和 approval-architecture.md 第十一节已有完整设计(在途固定版本、结构指纹 + 锁文件强制升版、找不到版本拒绝启动、显式迁移),但建立在"流程即代码"上,8b 的结论可能推翻它。
依赖:8b 定了,③ 才能定;① 和 ② 可以先讨论。阶段 2 的"同表多定义"和阶段 3 的"版本指纹与显式迁移"都等它。
流程定义要不要支持在 runtime 修改
要定的:① 哪些场景确实需要 runtime 修改:Forge 的流程定义存在数据库里、按项目分配、运行时可编辑;V3 的 workflow 也预期能在 runtime 更新。② 如果支持,和代码定义的流程是共用一个 lifecycle 内核,还是分成两套机制。
影响:8a 的版本方案(锁文件指纹、CI 检查、找不到版本拒绝启动)要不要重做;lifecycle 能不能同时承载 Forge,Forge 的评估把"定义即数据、按记录解析定义"列为第一条必须项;也和 #519 的内核 API 有关(定义注册目前同名不可替换)。
外层环节用生成的真实状态,还是只存数据的计划
影响:审批层原型怎么做。前者保留按环节计时和准确的按钮可用性,后者加签和升版更简单。PR #542 的现状就是后者,可以直接和前者在同一组场景上对比。
和 workflow 插件、AI employee 人机中断的关系
影响:任务模型要不要统一这几种"等人处理"的机制。现有的是 workflow 的节点挂起(resumeNode)和 AI employee 的 AgentInvokeInterrupt / resumeInvoke。需要先读这两个插件的实现。approval-architecture.md 第六节已经设计了"人与 AI 协同"走任务内核,可以作为起点。
统一待办是否覆盖审批以外的人工任务;人与 AI 协同用什么交互
影响:任务内核的范围。定义之前,任务内核的接口只保证不依赖审批特有的概念。
三、PR #519 合并前
12–15 会改变公开 API 的语义,发布后再改就是破坏性变更。可以整理成一份清单和 #519 的作者对。
create() 走守卫和校验
现在 create() 只检查初始状态,谁能创建只能在路由里管(缺口 9)。
已完成:定义新增 create: { validate, guard },runtime.create() 在事务内先校验值、再问守卫,拒绝格式与转换相同(INVALID_INPUT / GUARD_REJECTED)。示例插件的“只有客户/员工本人能建单”和字段检查移进了定义。PR #519 提交 d2fd0ef9。
请求号重放时比对转换名
现在同一个 requestId 用在不同转换上会被当成重放,吞掉一次真实操作(缺口 8)。
已完成:重放时比对转换名:同一 requestId 用在另一个转换上时抛 REQUEST_REUSED(HTTP 409),错误信息写明它已用于哪个转换,不改任何数据。文档补了错误表和排障条目。PR #519 提交 4276e599。
can() 带输入,校验先于守卫
现在 available() / can() 以空输入调用守卫,按输入区分的操作拿不到准确的可用性(缺口 10)。
已完成:fire() / planTransition() 改为先 validate 再问守卫;runtime.can(name, id, transition, actor, { input }) 带输入时先校验,返回 { allowed, blockers, problems },不抛错。available() 保持不带输入,文档说明按输入区分的操作要用 can() 逐项问。PR #519 提交 ed551ab8。
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。
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。
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。
修内存 store 的回滚,派发尊重 runAfter
回滚现在恢复整库快照,会抹掉事务外的写入;进程内派发忽略退避时间(缺口 11)。不修的话,并发测试的结论不可信。
已完成:内存 store 的事务改为撤销日志,回滚只撤销本事务的写入;@nocobase/lifecycle/testing 新增 InProcessDispatcher,按假时钟等待 runAfter,runDue() 执行到期重试,retries: 'immediate' 保留立即重试。PR #519 提交 e0157809。
四、后续技术路线
来自 improvement-plan.md 的改进点汇总。阶段 1 的小修复已并入上面的 12–15、18。
库:审批层的前置条件
内部转换、进入/离开写字段钩子、按字段计时和触发器过滤、{ to, values } 异步钩子、冻结字段、只画可达的边、同表多定义。
审批层原型
任务表和任务小状态机、策略库、defineApproval、待办 API、版本指纹与显式迁移。
验证
用审批层重写 772 行的原生合同审批和代表场景,对比代码量;在 PostgreSQL / MySQL 上验证唯一索引、"先锁父行"和额度扣减。