/** * module: process —— 本模块要外界提供的能力(编码规范 §10.1 / §5.3)。 */ import type { ProcessNode, ProcessPackage, StaffId, TerminalState, WorkflowState } from '@contracts'; import type { NodeInstance } from './internal/domain/owner-resolution.ts'; import type { ActivityContractFace } from './internal/domain/closure.ts'; export type Clock = { now(): Date; }; export type IdGenerator = { next(): string; }; /** 工作流实例的持久面 —— 崩溃后可续跑 */ export type WorkflowRecord = { readonly id: string; readonly packageId: string; readonly packageVersion: number; readonly subject: string; readonly state: WorkflowState; /** 来源:凡单必有来源(触发器 / 登记人 / 上游系统) */ readonly source: { kind: 'periodic' | 'event' | 'registration'; ref: string; by?: string; }; readonly initiation: Record; /** * 工作组绑定的**快照**(ADR-011):起单时按来源人装载,之后改组不影响这条单。 * 留痕要能解释「当时为什么派给他」,而不是被今天的组织调整改写(同 AM 9.1 版本信封)。 */ readonly workGroup?: Record; /** * 节点指派 `{ 节点id → 员工id }` —— **起单时定死某几个节点归谁**。 * 分派仍是运营时的事:这只是在轮值之前插一句「这个节点就他了」, * 指的人不在岗则退回轮值(单不能没人接)。测试台用它把整条流走给指定账号。 */ readonly assignments?: Record; /** 各节点输出口载荷 —— 边映射装配的来源 */ readonly ports: Record>; readonly vars: Record; readonly startedAt: string; readonly finishedAt?: string; }; /** * 流程运行时 —— **执行面的可替换切面**(M5-1):按结构调度节点、条件出边选路、 * 失败走向、审批裁决、状态聚合。自持状态机(internal/runtime/self-hosted-runtime) * 是默认实现;Mastra 编译产物(workflow-compiler)将是第二实现 —— * 八例端到端是两个实现的等价性护栏。 * 定义生命周期(保存/激活/校验)不在此内:那决定「编译哪一份定义」,与引擎选型无关。 */ export type FlowRuntime = { /** 起单后进入起点(起单前置 —— 校验/建单/留痕 —— 归 usecase,不在此内) */ begin(record: WorkflowRecord, pkg: ProcessPackage): Promise; onTaskTerminal(taskId: string, terminal: TerminalState, ports: Record>): Promise; applyFailure(workflowId: string, nodeId: string, instance: number, node: Extract, terminal: TerminalState): Promise; onApprovalVerdict(workflowId: string, nodeId: string, verdict: 'pass' | 'reject', payload: Record): Promise; refreshState(workflowId: string): Promise; terminate(workflowId: string, reason: string): Promise; }; export type ProcessStore = { savePackage(pkg: ProcessPackage): Promise; /** 就地写回一版的定义(编辑即其身)—— 撞不存在的版本报错,不静默插入 */ updatePackageDefinition(id: string, version: number, pkg: ProcessPackage): Promise; /** 钉在该版上的未完结工作流数 —— 在途钉版不可变的判据 */ countPinnedInFlight(id: string, version: number): Promise; /** 只改状态,不动定义 —— state 在列上与 definition 里各存一份,两处要一起改 */ setPackageState(id: string, version: number, state: 'active' | 'retired'): Promise; deletePackage(id: string, version: number): Promise; /** 改名及于这条流的所有版本 —— 名字是流的身份,不是版本的属性 */ renameFlow(id: string, name: string): Promise; getPackage(id: string, version?: number): Promise; listPackages(): Promise; insertWorkflow(record: WorkflowRecord): Promise; getWorkflow(id: string): Promise; listWorkflows(limit: number): Promise; updateWorkflow(id: string, patch: Partial): Promise; listInstances(workflowId: string): Promise; insertInstance(workflowId: string, instance: NodeInstance): Promise; updateInstance(workflowId: string, nodeId: string, instance: number, patch: Partial & { taskId?: string; }): Promise; findInstanceByTask(taskId: string): Promise<{ workflowId: string; nodeId: string; instance: number; } | undefined>; nodeOwners(packageId: string): Promise>; setNodeOwner(packageId: string, nodeId: string, staffId: string | undefined): Promise; }; /** 活动契约面 —— app 注入 `activity-registry`(L4):编排校验与起单都要它 */ export type ActivityDirectory = { faces(): Promise>; }; /** 岗位与在岗人员 —— app 注入 `org`(L1):负责人四策略解析的候选集 */ export type OrgDirectory = { positions(): Promise>; candidates(position: string): Promise>; /** 某人的工作组绑定(ADR-011)—— 起单时按来源人取,取不到即不装载 */ workGroupBinding(staffId: string): Promise> | undefined>; /** 在岗与否 —— 绑定的人已离岗就不硬用,退回四策略 */ isActive(staffId: string): Promise; }; /** 起单与终态 —— app 注入 `task`(L6) */ export type TaskGateway = { start(input: { activityId: string; ownerId: StaffId; title: string; workflowId: string; nodeId: string; data: Record; message?: string; }): Promise<{ id: string; }>; }; /** 审批点 —— app 注入 `human-tool`(L3):审批单卡到审批人工位 */ export type ApprovalGateway = { raise(input: { workflowId: string; nodeId: string; approverId: StaffId; subject: string; basis: ReadonlyArray<{ k: string; v: string; }>; }): Promise<{ id: string; }>; }; /** * 调用接口的入参校验 —— app 注入 ajv。 * 「凡单必有来源」的另一半是「凡单必合契约」:不合契约**不得起单**, * 且必须在**建工作流之前**判掉,否则会留下一条永远推不动的在途单。 */ export type SchemaValidator = { validate(schema: Record, value: unknown): { ok: boolean; errors: string[]; }; }; /** S-FEEL —— app 注入 `@sfeel`:流转零 LLM */ export type Sfeel = { condition(expression: string, scope: Readonly>): boolean; expression(expression: string, scope: Readonly>): unknown; }; /** 流转记入事件 —— app 注入 `event`(L1) */ export type FlowEventPublisher = { publish(input: { workflowId: string; kind: string; detail: Record; }): Promise; }; /** 终态回执 —— 登记人收消息 */ export type ReceiptNotifier = { notify(input: { to: StaffId; workflowId: string; subject: string; state: WorkflowState; }): Promise; }; export type { TerminalState };