Archived
feat(loop-engine): card 与 submit 两个动词;审核卡放宽到单选项
ADR-014 §8 原写「不发决策卡、不 submit」—— 那是假设还有 agent 在场时的保守话。 全程无 agent 的活动没有别人来干这两件事,不开口最后两步就没人做。 护栏没松:submit 仍要过提交门(判据全绿),系统提交不比模型提交宽松。 发过的卡不再发第二张;发了卡就收拍,排在后面的笔一律不跑。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LKMZDwMbyKzjw6Hembwvet
This commit is contained in:
@@ -165,7 +165,66 @@ export function createAutoRunner(deps: AutoRunnerDeps): {
|
||||
return { wrote: 0, dropped: 0 };
|
||||
}
|
||||
|
||||
throw new AutoRecipeError(index, `动词尚未实现:${action.kind}`);
|
||||
if (action.kind === 'card') {
|
||||
/*
|
||||
* **发过就不再发**(幂等)。人工激活的 auto 步,**配方发卡就是启动** ——
|
||||
* 不必再有一个「先 start」的礼仪:学不稳的礼仪就是死法(2026-08-02 已拆过一次)。
|
||||
*/
|
||||
if (context.carded) {
|
||||
await deps.memory.append({
|
||||
taskId: context.taskId,
|
||||
kind: 'observation',
|
||||
title: `这一步已经发过卡,不再发第二张`,
|
||||
detail: { auto: `${context.stepId}#${index + 1}`, internal: true },
|
||||
});
|
||||
return { wrote: 0, dropped: 0, stop: true };
|
||||
}
|
||||
const basis = (action.basis ?? []).map((b) => ({ k: b.k, v: evalText(b.v, base) }));
|
||||
if (action.as === 'decide') {
|
||||
const options = action.options ?? [];
|
||||
if (options.length === 0) throw new AutoRecipeError(index, '决策卡一个选项都没有');
|
||||
const { id } = await deps.cards.decide({
|
||||
taskId: context.taskId,
|
||||
ownerId: context.ownerId,
|
||||
humanCallCap: context.humanCallCap,
|
||||
ask: evalText(action.ask ?? action.title, base),
|
||||
options: options.map((o) => ({ value: o.value, label: o.label, ...(o.detail === undefined ? {} : { detail: o.detail }) })),
|
||||
basis,
|
||||
itemId: context.stepId,
|
||||
/* `CardGateway.decide` 的 variant 只认 'review'(审核卡);card.variant 的类型口子更宽(还有 'data'),
|
||||
那是给执行工单用的 —— 这里按值窄化,别把 'data' 误传给决策卡 */
|
||||
...(action.variant === 'review' ? { variant: action.variant } : {}),
|
||||
});
|
||||
return { wrote: 0, dropped: 0, cardId: id, stop: true };
|
||||
}
|
||||
const { id } = await deps.cards.issue({
|
||||
taskId: context.taskId,
|
||||
ownerId: context.ownerId,
|
||||
humanCallCap: context.humanCallCap,
|
||||
workOrder: 'execution',
|
||||
itemId: context.stepId,
|
||||
card: {
|
||||
mode: 'execution',
|
||||
title: evalText(action.title, base),
|
||||
ask: evalText(action.title, base),
|
||||
basis,
|
||||
/* 零输入:填报全在工作区的件上(2026-08-02 用户定) */
|
||||
fields: [],
|
||||
...(action.variant === 'data' ? { variant: 'data' } : {}),
|
||||
...(action.guide === undefined ? {} : { steps: [...action.guide] }),
|
||||
...(action.standard === undefined ? {} : { standard: evalText(action.standard, base) }),
|
||||
...(action.gateLedgers === undefined ? {} : { gateLedgers: [...action.gateLedgers] }),
|
||||
},
|
||||
});
|
||||
return { wrote: 0, dropped: 0, cardId: id, stop: true };
|
||||
}
|
||||
|
||||
if (action.kind === 'submit') {
|
||||
return { wrote: 0, dropped: 0, submit: { rationale: evalText(action.rationale, base) }, stop: true };
|
||||
}
|
||||
|
||||
/* 五个动词(say/project/analyze/card/submit)上面已全部处理,这里在类型上不可达 —— 留作穷尽性兜底 */
|
||||
throw new AutoRecipeError(index, '动词尚未实现');
|
||||
}
|
||||
|
||||
for (const [index, action] of recipe.do.entries()) {
|
||||
|
||||
Reference in New Issue
Block a user