M3(2/5): human-tool 与 release —— 卡协议与放行门
human-tool(L3): - 三规矩机检:调用必附上下文(裸调用拒发)/ 回传即留痕 / 完成只出自放行通过 - 工单两模式 + 对话卡族的形态校验与回传校验;驳回必附意见;补全卡「没法填」旁路; 决策卡自定义收尾项必附理由;已答不得再答(只增不改) - 到达面两路(负责人 → 任务对话流;他人 → 系统消息);驳回重答 = 新卡实例、原卡闭合 - 调人预算由基座在环外强制;落地失败卡 = 执行工单的 preset release(L4): - 落地预检五项(入参 schema / 工具可用与授权 / 连接可达 / 目标预校验 / 数据冲突) - 预检全过才发验收工单,卡面即已验证的动作清单;不过即打回环内,不发卡 - 放行写合并 + 幂等落地(任务 × 意向);落地收敛才发射输出口; 受阻走落地失败卡且 emittedPorts 恒空;已成动作留存不回滚 215 个测试通过,门禁五道全绿 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
This commit is contained in:
co-authored by
Claude Opus 5
parent
146e9bbfad
commit
561a2474ae
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* module: release —— 本模块要外界提供的能力(编码规范 §10.1 / §5.3)。
|
||||
*/
|
||||
import type { ActionIntent, StaffId, TaskId } from '@contracts';
|
||||
import type { LandingOutcome } from './internal/domain/precheck.ts';
|
||||
|
||||
export type Clock = { now(): Date };
|
||||
export type IdGenerator = { next(): string };
|
||||
|
||||
export type IntentStore = {
|
||||
save(intent: ActionIntent): Promise<void>;
|
||||
list(taskId: TaskId): Promise<ActionIntent[]>;
|
||||
setState(id: string, state: ActionIntent['state'], error?: string): Promise<void>;
|
||||
};
|
||||
|
||||
/** 台账 —— app 注入 `ledger`(L1);放行写与冲突检查都在它那 */
|
||||
export type LedgerGateway = {
|
||||
checkConflicts(taskId: TaskId): Promise<Array<{ field: string; key: Record<string, string | number> }>>;
|
||||
releaseWrite(taskId: TaskId): Promise<{ mergedRows: number; emittedEvents: number }>;
|
||||
};
|
||||
|
||||
/** 工具 —— app 注入 `toolbox`(L2);预检的②③④项与落地执行都经它 */
|
||||
export type ToolGateway = {
|
||||
/** 工具可用(未停用)与授权有效 */
|
||||
isInvocable(tool: string, activityId: string): Promise<{ ok: boolean; reason?: string }>;
|
||||
/** 入参 schema 校验 */
|
||||
validateInput(tool: string, input: Record<string, unknown>): Promise<{ ok: boolean; errors: string[] }>;
|
||||
/** 目标预校验:工具契约含 validate 能力时调之(ADR-005) */
|
||||
validateTarget(input: {
|
||||
tool: string;
|
||||
activityId: string;
|
||||
taskId: TaskId;
|
||||
payload: Record<string, unknown>;
|
||||
}): Promise<{ ok: boolean; error?: string } | undefined>;
|
||||
/** 落地执行 —— 附幂等键 */
|
||||
execute(input: {
|
||||
tool: string;
|
||||
activityId: string;
|
||||
taskId: TaskId;
|
||||
payload: Record<string, unknown>;
|
||||
idempotencyKey: string;
|
||||
}): Promise<LandingOutcome>;
|
||||
};
|
||||
|
||||
/** 人类工具 —— app 注入 `human-tool`(L3):发验收工单与落地失败卡 */
|
||||
export type CardGateway = {
|
||||
issueAcceptance(input: {
|
||||
taskId: TaskId;
|
||||
ownerId: StaffId;
|
||||
humanCallCap: number;
|
||||
title: string;
|
||||
basis: ReadonlyArray<{ k: string; v: string }>;
|
||||
deliverable: string;
|
||||
criteria: ReadonlyArray<{ label: string; level: 'L1' | 'L2' | 'L3' | 'L4' | 'L5'; pass: boolean }>;
|
||||
actions: ReadonlyArray<{ label: string; effect: 'read' | 'write'; detail: string }>;
|
||||
}): Promise<{ id: string }>;
|
||||
issueLandingFailure(input: {
|
||||
taskId: TaskId;
|
||||
ownerId: StaffId;
|
||||
humanCallCap: number;
|
||||
failures: ReadonlyArray<{ label: string; reason: string }>;
|
||||
}): Promise<{ id: string }>;
|
||||
};
|
||||
|
||||
/** 任务上下文 —— app 注入(活动 ID 与契约的输出口声明) */
|
||||
export type TaskContext = {
|
||||
activityIdOf(taskId: TaskId): Promise<string>;
|
||||
outputPortsOf(taskId: TaskId): Promise<{ mandatory: string[]; selective: Array<{ group: string; ports: string[] }> }>;
|
||||
};
|
||||
|
||||
/** 输出口发射 —— app 注入 `event`(L1):落地收敛**之后**才发 */
|
||||
export type PortEmitter = {
|
||||
emit(input: { taskId: TaskId; port: string; payload: Record<string, unknown> }): Promise<void>;
|
||||
};
|
||||
|
||||
export type MemoryRecorder = {
|
||||
append(input: {
|
||||
taskId: TaskId;
|
||||
kind: 'action-intent' | 'release-verdict' | 'ledger-write';
|
||||
title: string;
|
||||
detail?: Record<string, unknown>;
|
||||
}): Promise<void>;
|
||||
};
|
||||
Reference in New Issue
Block a user