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:
xcs
2026-08-27 23:20:16 +08:00
co-authored by Claude Fable 5
parent 909b1fe4b7
commit 7b85a79323
79 changed files with 3903 additions and 1297 deletions
@@ -197,7 +197,13 @@ export function createHumanToolService(deps: HumanToolServiceDeps): HumanToolSer
const at = deps.clock.now().toISOString();
const result = reply.mode === 'acceptance' ? reply.verdict : 'submitted';
const values = reply.mode === 'acceptance' ? reply.values : reply.values;
await deps.cards.markWorkOrderDone(reply.workOrderId, { at, by, result, values });
await deps.cards.markWorkOrderDone(reply.workOrderId, {
at,
by,
result,
values,
...(reply.mode === 'acceptance' && reply.checked !== undefined ? { checked: [...reply.checked] } : {}),
});
/*
* 出口:**算出来的优先,而且在服务端算数**(2026-08-02 修)。
@@ -365,6 +371,15 @@ export function createHumanToolService(deps: HumanToolServiceDeps): HumanToolSer
return deps.cards.listChatCards(taskId);
},
async pendingCards(taskId) {
const orders = (await deps.cards.listWorkOrders(taskId, true)).filter((o) => o.done === undefined);
const chats = (await deps.cards.listOpenChatCards(taskId)).filter((c) => !chatCardAnswered(c));
return [
...orders.map((o) => ({ id: String(o.id), title: o.title, kind: 'work-order' as const })),
...chats.map((c) => ({ id: String(c.id), title: c.ask, kind: 'chat-card' as const })),
];
},
async openCardsOf(staffId) {
return deps.cards.openCardsOf(staffId);
},
@@ -375,7 +390,13 @@ function applyChatReply(card: ChatCard, reply: ChatCardReply, by: StaffId, at: s
if (card.k === 'decision' && reply.k === 'decision') {
return {
...card,
chosen: { value: reply.value, at, by, ...(reply.note === undefined ? {} : { note: reply.note }) },
chosen: {
value: reply.value,
at,
by,
...(reply.note === undefined ? {} : { note: reply.note }),
...(reply.checked === undefined ? {} : { checked: [...reply.checked] }),
},
};
}
if (card.k === 'elicitation' && reply.k === 'elicitation') {
@@ -392,3 +413,9 @@ function applyChatReply(card: ChatCard, reply: ChatCardReply, by: StaffId, at: s
}
return card;
}
/** 对话卡答没答:决策卡看 chosen,其余(录入 / 补全 / 评价)看 done */
function chatCardAnswered(card: ChatCard): boolean {
if (card.k === 'decision') return card.chosen !== undefined;
return 'done' in card && card.done !== undefined;
}