Archived
harness 与工作台同日修:数据工单确认即入库 · 叫了人不再自动推进(两道结构闸)· 审核卡 = 终局验收卡结构 · 验收清单人点达标 · 迟到守卫不丢放行裁决 · 画面跟着这一拍发出去的卡走 · 成果详情只翻页 · 占位居中 · 进度条
human-tool / ledger / loop-engine / release / task / interaction 六模块与 app 层(desk 路由、engines、consumers、 data-order-landing、case-stamp、reset-runs、seed-activities、容器与 server 装配),web 的 desk-task (ViewRegion / chat-bubbles / review-face)与 app.css、ui-kit 图标;契约 card / manifest; 迁移 0034_task_seq 与迁移日志;api-report 快照;CLAUDE.md 快照、debt、活动开发规范、HTML 报告规范同步。 逐条口径见 CLAUDE.md 快照(2026-08-27 下午至晚)。另一会话做的,一直留在工作树没入库;与同批另两条提交互相依赖。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0167qWjeyBwvfdGovd7FWbCu
This commit is contained in:
@@ -131,6 +131,16 @@ export function checkWorkOrderReply(order: WorkOrder, reply: WorkOrderReply): Ca
|
||||
* 为什么拦在这里而不是落地(land):回传是同步收单、落地在推进器里异步跑,
|
||||
* 拦晚了就是「工单已答完、任务卡死、推进器循环报错」(实测)。
|
||||
*/
|
||||
/*
|
||||
* **人工验收清单要点全才能通过**(2026-08-27 用户定):卡上那几条是人核这份工单要走的步骤,
|
||||
* 按钮灰掉只是给人看的,这里才是门 —— 直调 API 把「通过」交回来也得把清单点全。
|
||||
*/
|
||||
if (reply.verdict === 'pass') {
|
||||
const missing = checklistMissing((order as AcceptanceWorkOrder).checklist, reply.checked);
|
||||
if (missing.length > 0) {
|
||||
out.push({ code: 'checklist-incomplete', message: `验收清单还有 ${missing.length} 条待确认:${missing[0] ?? ''}` });
|
||||
}
|
||||
}
|
||||
const choice = (order as AcceptanceWorkOrder).portChoice;
|
||||
if (choice !== undefined && reply.verdict === 'pass') {
|
||||
const picked = reply.selectedPorts[choice.group];
|
||||
@@ -154,6 +164,20 @@ export function checkWorkOrderReply(order: WorkOrder, reply: WorkOrderReply): Ca
|
||||
return out;
|
||||
}
|
||||
|
||||
/** 清单里还没点成「达标」的那几条;没有清单就是空 */
|
||||
export function checklistMissing(checklist: ReadonlyArray<string> | undefined, checked: ReadonlyArray<string> | undefined): string[] {
|
||||
const done = new Set(checked ?? []);
|
||||
return (checklist ?? []).filter((item) => !done.has(item));
|
||||
}
|
||||
|
||||
/**
|
||||
* 审核卡上哪一个选项是「通过」—— 与前端 `review-face.ts` 同一口径:值叫 `approve` 的,否则推荐的那个,否则第一个。
|
||||
* 清单只拦这一个选项:驳回不需要把清单点全。
|
||||
*/
|
||||
export function reviewApproveValue(card: Extract<ChatCard, { k: 'decision' }>): string | undefined {
|
||||
return (card.options.find((o) => o.value === 'approve') ?? card.options.find((o) => o.recommended === true) ?? card.options[0])?.value;
|
||||
}
|
||||
|
||||
export function checkChatCardReply(card: ChatCard, reply: ChatCardReply): CardViolation[] {
|
||||
const out: CardViolation[] = [];
|
||||
if (card.id !== reply.cardId) return [{ code: 'mismatch', message: '回传的卡 ID 与卡不符' }];
|
||||
@@ -161,6 +185,11 @@ export function checkChatCardReply(card: ChatCard, reply: ChatCardReply): CardVi
|
||||
|
||||
if (card.k === 'decision' && reply.k === 'decision') {
|
||||
if (card.chosen !== undefined) return [{ code: 'already-answered', message: '该决策已选,只增不改' }];
|
||||
// 审核卡穿验收工单的衣服:选「通过」也得把人工验收清单点全(与验收工单同一道门)
|
||||
if (reply.value === reviewApproveValue(card)) {
|
||||
const missing = checklistMissing(card.checklist, reply.checked);
|
||||
if (missing.length > 0) return [{ code: 'checklist-incomplete', message: `验收清单还有 ${missing.length} 条待确认:${missing[0] ?? ''}` }];
|
||||
}
|
||||
const known = card.options.some((o) => o.value === reply.value);
|
||||
// 自定义收尾项:allowCustom 打开时允许选项之外的处置(必附理由)
|
||||
if (!known && !card.allowCustom) out.push({ code: 'unknown-option', message: `分支不在选项内:${reply.value}` });
|
||||
|
||||
Reference in New Issue
Block a user