Archived
用户定的形态:测试台是**后端检测活动与流程**的,真实任务照旧到工作台
(不然指定账号干嘛)—— 所以测试台只管「起单 + 看穿」,人那一环还是真的。
**节点指派**(迁移 0016 workflow.assignments):
- StartWorkflowInput 增 `assignments {节点id → 员工id}`;分派改三级取人:
①节点指派(点名的人在岗就是他)②员工组轮值 ③全员轮值 —— 指派是加法不是替换,
点名的人不在岗退回轮值(单不能没人接);事件里如实记 `(节点指派)`
- **顺带修一个真 bug**:`node_instance.assignee_id` 写了但 `listInstances` 从没读回来
(审批箱收件、员工面链条、治理列表全按它认人 —— 写了不读等于没写)
**全链 Trace**(新模块 trace,L2;迁移 0017 sys.trace_span):
- span 由 **Mastra Observability 组件**产出(执行不经 Mastra 图,所以手动打点),
一开就落库、一收就补 —— 观测面不该比被观测的事晚(不走 exporter 的批量异步路)
- 打点覆盖:工作流 root → 节点(记着派给了谁)→ 活动内每次工具调用 / 模型调用 / 人卡
- **root span 挂在 `runtime.begin`**:那是「单已建好、还没进第一个节点」那一刻 ——
挂在 start 返回之后就晚了,起单同步走到第一个节点,节点 span 会先出生、另起孤树(实测)
- 观测尽力而为:写 span 失败吞掉,不掀翻业务
- 三样东西各管各的:trace(机器读时间轴)/ 任务记忆(人读留痕)/
**工作区报告(活动自持的 HTML)** —— 谁也不替代谁
路由:`POST /api/gov/testbed/run`(带指派起单)、`GET /api/gov/workflows/:id/trace`。
端到端六例(真库):指派生效 / 任务真落到被指派人手上并由他答单 / 下游节点同样生效 /
全链 span 逐层挂得上且耗时齐备 / 一条流一个 traceId / 不指派照旧轮值。五闸全过。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
66 lines
2.6 KiB
TypeScript
66 lines
2.6 KiB
TypeScript
/**
|
|
* process 的 sys schema 表(编码规范 §7 / ADR-002)。
|
|
* 流程包定义与版本 + 工作流实例 + 节点实例(重开产生新实例,旧实例不再入算)。
|
|
*/
|
|
import { boolean, index, integer, jsonb, primaryKey, text, timestamp } from 'drizzle-orm/pg-core';
|
|
import { sys } from '@shared/db.ts';
|
|
|
|
export const processPackageTable = sys.table(
|
|
'process_package',
|
|
{
|
|
id: text('id').notNull(),
|
|
version: integer('version').notNull(),
|
|
name: text('name').notNull(),
|
|
state: text('state').notNull().default('draft'),
|
|
definition: jsonb('definition').notNull(),
|
|
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
|
publishedAt: timestamp('published_at', { withTimezone: true }),
|
|
},
|
|
(t) => [primaryKey({ columns: [t.id, t.version] }), index('process_package_state_idx').on(t.state)],
|
|
);
|
|
|
|
export const workflowTable = sys.table(
|
|
'workflow',
|
|
{
|
|
id: text('id').primaryKey(),
|
|
packageId: text('package_id').notNull(),
|
|
packageVersion: integer('package_version').notNull(),
|
|
subject: text('subject').notNull(),
|
|
state: text('state').notNull().default('running'),
|
|
/** 凡单必有来源 */
|
|
source: jsonb('source').notNull(),
|
|
initiation: jsonb('initiation').notNull(),
|
|
/** 工作组绑定快照(ADR-011)—— 起单时装载,之后改组不影响这条单 */
|
|
workGroup: jsonb('work_group'),
|
|
/** 节点指派:{ 节点id → 员工id } —— 测试台定向;生产起单不传,照旧轮值 */
|
|
assignments: jsonb('assignments'),
|
|
ports: jsonb('ports').notNull().default({}),
|
|
vars: jsonb('vars').notNull().default({}),
|
|
startedAt: timestamp('started_at', { withTimezone: true }).notNull().defaultNow(),
|
|
finishedAt: timestamp('finished_at', { withTimezone: true }),
|
|
},
|
|
(t) => [index('workflow_state_idx').on(t.state, t.startedAt)],
|
|
);
|
|
|
|
export const nodeInstanceTable = sys.table(
|
|
'node_instance',
|
|
{
|
|
workflowId: text('workflow_id').notNull(),
|
|
nodeId: text('node_id').notNull(),
|
|
instance: integer('instance').notNull(),
|
|
running: boolean('running').notNull().default(false),
|
|
waiting: boolean('waiting').notNull().default(false),
|
|
activated: boolean('activated').notNull().default(true),
|
|
terminal: text('terminal'),
|
|
failureDigested: boolean('failure_digested'),
|
|
taskId: text('task_id'),
|
|
approvalId: text('approval_id'),
|
|
assigneeId: text('assignee_id'),
|
|
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
|
},
|
|
(t) => [
|
|
primaryKey({ columns: [t.workflowId, t.nodeId, t.instance] }),
|
|
index('node_instance_task_idx').on(t.taskId),
|
|
],
|
|
);
|