/** * 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; list(taskId: TaskId): Promise; setState(id: string, state: ActionIntent['state'], error?: string): Promise; }; /** 台账 —— app 注入 `ledger`(L1);放行写与冲突检查都在它那 */ export type LedgerGateway = { checkConflicts(taskId: TaskId): Promise; }>>; releaseWrite(taskId: TaskId): Promise<{ mergedRows: number; emittedEvents: number; /** 试跑单被挡在正式表外的主数据行数(非试跑恒为 0)—— 放行痕要如实说 */ simulatedRows: number; /** 早已在数据工单确认时入库、这次不再写的行数 —— 放行痕上分开说,「合并 N 行」不多报 */ landedEarlierRows: number; /** 合并进正式版的行 —— 输出口发射的载荷取自这里 */ merged: ReadonlyArray<{ domain: string; object: string; key: Record; values: Record; }>; }>; }; /** 工具 —— app 注入 `toolbox`(L2);预检的②③④项与落地执行都经它 */ export type ToolGateway = { /** 工具可用(未停用)与授权有效 */ isInvocable(tool: string, activityId: string): Promise<{ ok: boolean; reason?: string; }>; /** 入参 schema 校验 */ validateInput(tool: string, input: Record): Promise<{ ok: boolean; errors: string[]; }>; /** 目标预校验:工具契约含 validate 能力时调之(ADR-005) */ validateTarget(input: { tool: string; activityId: string; taskId: TaskId; payload: Record; }): Promise<{ ok: boolean; error?: string; } | undefined>; /** 落地执行 —— 附幂等键 */ execute(input: { tool: string; activityId: string; taskId: TaskId; payload: Record; idempotencyKey: string; }): Promise; }; /** 人类工具 —— 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; settled?: boolean | undefined; rationale?: string | undefined; }>; actions: ReadonlyArray<{ label: string; effect: 'read' | 'write'; detail: string; }>; /** 人工验收清单 —— 原样带上卡 */ checklist?: ReadonlyArray | undefined; /** 这张卡打回得了吗(`false` = 那颗钮不摆)—— 端口不声明它,值就在装配层被丢掉,这行不能省 */ redoable?: boolean | undefined; /** * 可修订字段 —— 验收报告「AI 已填好、可编辑」改的就是它们(带 `ai` 拟值与 `ledger` 落回地址)。 * **端口不声明它,值就在装配层被丢掉**:上游一路传下来,而 TypeScript 对条件展开 * 不做多余属性检查,于是编译不报、运行时静默丢 —— 卡面 `fields` 恒空(踩过)。 */ fields?: ReadonlyArray>; /** * 验收页的正文块 —— 绑台账的可编辑成果表走这里。 * **端口不声明它,值就在装配层被丢掉**(与 `fields` 同一个坑,又踩了一次: * 契约声明了、submit 传了、release 转了,到装配层那个显式对象里被抹平, * 编译不报、运行时静默丢 —— 卡面 blocks 恒空)。 */ blocks?: ReadonlyArray>; }): 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; 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; }): Promise; }; export type MemoryRecorder = { append(input: { taskId: TaskId; kind: 'action-intent' | 'release-verdict' | 'ledger-write'; title: string; detail?: Record; }): Promise; };