feat(engine): node.finished 全事件补 attempt 审计字段(无人值守审批对账:对面 actionId=runId:stepId:attempt)
目标:审计侧可对账'第几次尝试成功/失败',为无人值守高危动作的审批绑定提供键 代码逻辑:六处 node.finished 发射点统一带 state.attempt(初值1,重试循环内递增);可选字段向后兼容,无事件对象精确断言受影响 验证:引擎域 24/24 全绿 + 新增契约测试(重试3次终态 attempt=3、跳过=1、全事件 ≥1);typecheck 归零 风险与回滚:低——纯新增可选字段,revert 即回
This commit is contained in:
1 parent
10d125ce9d
commit
43ae705629
2 files changed
+36
-5
No files matched your search
@@ -255,14 +255,14 @@ async function executeNode(node: FlowNode, ctx: RunContext, states: Map<string,
|
||||
state.status = "failed";
|
||||
state.error = error.toRunError();
|
||||
state.finishedAt = new Date().toISOString();
|
||||
ctx.emit({ type: "node.finished", nodeId: node.id, status: "failed", error: state.error, durationMs: 0, evidence: [] });
|
||||
ctx.emit({ type: "node.finished", nodeId: node.id, status: "failed", error: state.error, durationMs: 0, evidence: [], attempt: state.attempt });
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (node.enabled === false) {
|
||||
state.status = "skipped";
|
||||
state.finishedAt = new Date().toISOString();
|
||||
ctx.emit({ type: "node.finished", nodeId: node.id, status: "skipped", durationMs: 0, evidence: [] });
|
||||
ctx.emit({ type: "node.finished", nodeId: node.id, status: "skipped", durationMs: 0, evidence: [], attempt: state.attempt });
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -371,6 +371,7 @@ async function executeNode(node: FlowNode, ctx: RunContext, states: Map<string,
|
||||
checkpointId: node.id,
|
||||
resumable,
|
||||
executor: "local",
|
||||
attempt: state.attempt,
|
||||
...(resumable ? { idempotencyKey: `${ctx.runId}:${node.id}` } : {})
|
||||
});
|
||||
return result;
|
||||
@@ -379,7 +380,7 @@ async function executeNode(node: FlowNode, ctx: RunContext, states: Map<string,
|
||||
state.status = "skipped";
|
||||
state.finishedAt = new Date().toISOString();
|
||||
state.durationMs = Date.now() - startMs;
|
||||
ctx.emit({ type: "node.finished", nodeId: node.id, status: "skipped", durationMs: state.durationMs, evidence: [] });
|
||||
ctx.emit({ type: "node.finished", nodeId: node.id, status: "skipped", durationMs: state.durationMs, evidence: [], attempt: state.attempt });
|
||||
throw error;
|
||||
}
|
||||
const runtimeError = asRuntimeError(error, node, component.id);
|
||||
@@ -395,7 +396,8 @@ async function executeNode(node: FlowNode, ctx: RunContext, states: Map<string,
|
||||
status: "cancelled",
|
||||
error: runtimeError,
|
||||
durationMs: state.durationMs,
|
||||
evidence: []
|
||||
evidence: [],
|
||||
attempt: state.attempt
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
@@ -412,7 +414,8 @@ async function executeNode(node: FlowNode, ctx: RunContext, states: Map<string,
|
||||
status: state.status,
|
||||
error: runtimeError,
|
||||
durationMs: state.durationMs,
|
||||
evidence: []
|
||||
evidence: [],
|
||||
attempt: state.attempt
|
||||
});
|
||||
if (skip) return undefined;
|
||||
throw error;
|
||||
|
||||
Reference in new issue
Block a user