选择组的出口选择要走到落地:恰发一口,随放行一并确认

A6 的输出是**选择组**(negotiation: won / revise / lost),回传协议里本来就有
`selectedPorts`,human-tool 也接住了 —— 但落地那一步硬写 `selectedPorts: {}`,
选择从未抵达。结果:放行了、台账写了,却「发射输出口:(无)」,
G4 异或网关拿不到 outcome,整条流悄无声息地停住。

按 verdict 同样的路子把它串起来:人事件载荷 → 末步运行时 → 谱走完时报上去 →
落地收敛时照它发射。

gates: tsc / eslint / depcruise / 399 测试 全绿

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
This commit is contained in:
2026-07-27 05:34:13 +08:00
co-authored by Claude Opus 5
parent 3af6252394
commit 691c61b6b8
7 changed files with 44 additions and 4 deletions
@@ -20,6 +20,7 @@ import type { FlowPosition } from '../domain/step-sequence.ts';
import {
checkDeclaration,
fallbackFields,
lastSelectedPorts,
lastStepIsAcceptance,
lastVerdictPassed,
failureAction,
@@ -257,7 +258,8 @@ export function createFlowRunner(deps: FlowRunnerDeps): FlowEngineService {
// 末步是验收工单时,终态由其**裁决**给出 —— 完成只出自放行通过。
// 裁决记在末步的 runtime 上(resume 时落笔),谱走完在这里读出来。
const released = lastStepIsAcceptance(model.steps) && lastVerdictPassed(position);
return { position, status: 'finished', released };
// 出口选择随放行一并确认,落地收敛时照它发射
return { position, status: 'finished', released, selectedPorts: lastSelectedPorts(position) };
}
case 'skip': {
await deps.memory.append({
@@ -295,12 +297,19 @@ export function createFlowRunner(deps: FlowRunnerDeps): FlowEngineService {
});
// 放行裁决落笔在这一步的运行时上:谱走完时据此定终局
const verdict = event.kind === 'human' ? String(event.payload['verdict'] ?? '') : '';
const chosen = event.payload['selectedPorts'];
const selectedPorts =
typeof chosen === 'object' && chosen !== null ? (chosen as Record<string, string>) : undefined;
if (step !== undefined && event.kind === 'human') {
// 人事件的载荷是**回传协议**的形状(`{mode, workOrderId, values}`),
// 出参在 `values` 里;直接把整个载荷当出参写,落进去的会是协议壳而不是答案。
await writeOutput(taskId, step, replyValuesOf(event.payload));
}
const next = markStep(position, index, verdict === '' ? { state: 'done' } : { state: 'done', verdict });
const next = markStep(
position,
index,
verdict === '' ? { state: 'done' } : { state: 'done', verdict, ...(selectedPorts === undefined ? {} : { selectedPorts }) },
);
await deps.state.save(taskId, model, next);
return { position: next, status: 'running' };
},