M5(2/3): process 用例与流转 —— 流程引擎的确定性调度

- routing domain:选边(XOR 恰取一条 + 默认分支兜底;AND 全走)、边映射装配(四类来源 +
  S-FEEL 转换)、AND 汇合回齐(容忍分支不阻汇合)、审批点驳回走向声明式
- process-service:起单 → 调度节点 → 负责人四策略解析(结果与依据留痕)→
  活动起单 / 审批点卡工位 / 终点收束按来源回执;任务终态到达继续流转或走失败走向四选一;
  审批驳回按声明回指定节点重开新实例;状态每次变动后重算聚合投影
- adapter:流程包(id × version)、工作流实例、节点实例三表持久 —— 崩溃后可续跑

ADR 待补项登记为 debt M5-1:§6.1 的 Mastra workflow 编译器作为可替换 adapter 后补,
当前由自持确定性状态机承担,语义等价、usecase 不动

325 个测试通过,门禁五道全绿

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
This commit is contained in:
JCAI Main Agent
2026-07-27 02:01:08 +08:00
co-authored by Claude Opus 5
parent 291fc39179
commit a8f6eef610
15 changed files with 4072 additions and 10 deletions
+120
View File
@@ -0,0 +1,120 @@
/**
* module: process —— 本模块要外界提供的能力(编码规范 §10.1 / §5.3)。
*/
import type { 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<string, unknown>;
/** 各节点输出口载荷 —— 边映射装配的来源 */
readonly ports: Record<string, Record<string, unknown>>;
readonly vars: Record<string, unknown>;
readonly startedAt: string;
readonly finishedAt?: string;
};
export type ProcessStore = {
savePackage(pkg: ProcessPackage): Promise<void>;
getPackage(id: string, version?: number): Promise<ProcessPackage | undefined>;
listPackages(): Promise<ProcessPackage[]>;
publishPackage(id: string, version: number): Promise<void>;
insertWorkflow(record: WorkflowRecord): Promise<void>;
getWorkflow(id: string): Promise<WorkflowRecord | undefined>;
listWorkflows(limit: number): Promise<WorkflowRecord[]>;
updateWorkflow(id: string, patch: Partial<WorkflowRecord>): Promise<WorkflowRecord>;
listInstances(workflowId: string): Promise<NodeInstance[]>;
insertInstance(workflowId: string, instance: NodeInstance & {
taskId?: string;
approvalId?: string;
}): Promise<void>;
updateInstance(workflowId: string, nodeId: string, instance: number, patch: Partial<NodeInstance> & {
taskId?: string;
}): Promise<void>;
findInstanceByTask(taskId: string): Promise<{
workflowId: string;
nodeId: string;
instance: number;
} | undefined>;
};
/** 活动契约面 —— app 注入 `activity-registry`(L4):编排校验与起单都要它 */
export type ActivityDirectory = {
faces(): Promise<Map<string, ActivityContractFace>>;
};
/** 岗位与在岗人员 —— app 注入 `org`(L1):负责人四策略解析的候选集 */
export type OrgDirectory = {
positions(): Promise<Set<string>>;
candidates(position: string): Promise<Array<{
staffId: string;
name: string;
openTasks: number;
}>>;
};
/** 起单与终态 —— app 注入 `task`(L6) */
export type TaskGateway = {
start(input: {
activityId: string;
ownerId: StaffId;
title: string;
workflowId: string;
nodeId: string;
data: Record<string, unknown>;
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;
}>;
};
/** S-FEEL —— app 注入 `@sfeel`:流转零 LLM */
export type Sfeel = {
condition(expression: string, scope: Readonly<Record<string, unknown>>): boolean;
expression(expression: string, scope: Readonly<Record<string, unknown>>): unknown;
};
/** 流转记入事件 —— app 注入 `event`(L1) */
export type FlowEventPublisher = {
publish(input: {
workflowId: string;
kind: string;
detail: Record<string, unknown>;
}): Promise<void>;
};
/** 终态回执 —— 登记人收消息 */
export type ReceiptNotifier = {
notify(input: {
to: StaffId;
workflowId: string;
subject: string;
state: WorkflowState;
}): Promise<void>;
};
export type { TerminalState };