fix(w05): 验收工单的面孔在装配层被丢掉 —— 两头都有测,中间那一段把值扔了

用户实测:「你怎么给我发成了执行工单,我需要的是验收工单,然后标题那一块需要是
验收卡的那种绿色」。

一、面孔:engines.ts 发执行工单那一处是一句写死只认 'data' 的三目,配方递下来的
'accept' 静默丢掉 —— 对话区照旧摆「执行工单」。**配方那一头有测(variant 原样递出去)、
前端那一头有测(accept 该穿哪副面孔)、e2e 按标题驱动也全绿,唯独中间没有。**
修:抽成纯函数 executionCardVariant(与旁边 decisionCardFace 同一种做法、同一个测试位),
认 data / accept、拒 review、认不出的当没写;4 测变异验证过。
e2e 同批改**认面孔不认标题**(试跑观测面的 cards 补 variant,契约变更)——
标题是活动自己写的字符串,卡穿哪副面孔由装配层决定,那一次正是两者对不上。

二、颜色:件① 验收工单那一副整套强调色换成验收绿 #0aa294(令牌从 ACCENT_LEDGER
现改选择器得来,与件②③、对话区验收卡一处真源),题头那条写死的浅蓝渐变单独给一条绿的;
数据工单那一副仍是蓝。真渲染核过两副 class 与两套令牌。

验收:e2e w05 5/5 released、被拒 0(面孔断言这一轮真在跑);2563 测、四门禁绿;
2.16.0 装库。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LKMZDwMbyKzjw6Hembwvet
This commit is contained in:
xcs
2026-08-31 23:26:09 +08:00
co-authored by Claude Fable 5
parent ce72da77eb
commit 445aa5ef22
11 changed files with 130 additions and 5 deletions
@@ -0,0 +1,37 @@
/**
* **执行工单的面孔在装配层这一处定**(2026-08-31,用户实测:「你怎么给我发成了执行工单,
* 我需要的是验收工单」)。
*
* 病:配方递下来 `variant: 'accept'`(验收工单 —— 件只读、人只确认),而装配层这一处
* 是一句写死只认 `'data'` 的三目,别的值**静默丢掉** —— 对话区照旧摆「执行工单」。
*
* 更值得记的是**两头都有测,唯独中间没有**:配方那一头断言了「variant 原样递出去」,
* 前端那一头断言了「accept 该穿哪副面孔」,e2e 按卡的**标题**驱动也照样绿 ——
* 三处都对,而它们中间这一段把值扔了。**面孔要在真发卡那条路上断言。**
*/
import { describe, expect, it } from 'vitest';
import { executionCardVariant } from '../wiring/engines.ts';
describe('执行工单的两副变体面孔', () => {
it('data 数据工单 / accept 验收工单,两个都认', () => {
expect(executionCardVariant('data', undefined)).toBe('data');
expect(executionCardVariant('accept', undefined)).toBe('accept');
});
it('剧本声明优先于卡骨架 —— 模型抄卡会把 variant 抄丢(2026-08-27 实测)', () => {
expect(executionCardVariant(undefined, 'data')).toBe('data');
expect(executionCardVariant('data', 'accept')).toBe('accept');
});
it('`review` 是决策卡那一路的面孔,不许串到执行工单上', () => {
expect(executionCardVariant('review', undefined)).toBeUndefined();
expect(executionCardVariant(undefined, 'review')).toBeUndefined();
});
it('没写 / 认不出的值:普通执行工单,不把脏东西端下去', () => {
expect(executionCardVariant(undefined, undefined)).toBeUndefined();
expect(executionCardVariant('', undefined)).toBeUndefined();
expect(executionCardVariant({ variant: 'accept' }, undefined)).toBeUndefined();
expect(executionCardVariant(42, undefined)).toBeUndefined();
});
});
@@ -712,6 +712,7 @@ export function registerGovernanceOpsRoutes(app: Hono<AppEnv>, container: Contai
cards: orders.map((o) => ({
id: o.id,
mode: o.mode,
...(o.mode === 'execution' && o.variant !== undefined ? { variant: o.variant } : {}),
title: o.title,
done: o.done !== undefined,
...(o.done?.result === undefined ? {} : { result: String(o.done.result) }),
+22 -2
View File
@@ -242,6 +242,27 @@ export function createOnlyViolation(
* 而 `checkChatCardReply` 又要求 `checked ⊇ checklist`:卡按不下去。**只在 `reviewFace` 为真时**
* 才退回剧本声明的清单 —— 非审核面孔的卡,清单就是没有,不发明一条按不下去的。
*/
/**
* **执行工单的面孔** —— 两副变体:`data` 数据工单(件上的表就是要建的记录,确认即入库)/
* `accept` 验收工单(件只读,人只核对确认)。`review` 是决策卡那一路的面孔,不许串到这儿来;
* 认不出的值一律当没写(普通执行工单),不把脏东西端下去。
*
* **剧本声明(`PlanStep.cardVariant`)优先于卡骨架**:模型抄卡时会把 `variant` 抄丢
* (2026-08-27 实测:同一张卡 gateLedgers 抄到了、variant 没有)。但卡自己带的值同样作数 ——
* 配方那一路没有模型可抄丢,而**一个步骤里两笔条件互斥的 card**(缺东西派数据工单 /
* 都齐了派验收工单,2026-08-31)本来就不可能由一个 step 级的 `cardVariant` 说清。
*
* **这一处此前是内联的三目,写死只认 `'data'`**(2026-08-31 用户实测:「你怎么给我发成了
* 执行工单」)—— 配方递下来的 `accept` 在这儿被静默丢掉,对话区照旧摆「执行工单」。
* 配方那一头与前端那一头都有测,唯独**真发卡这条路**上没有:面孔要在这里断言。
*/
export function executionCardVariant(fromCard: unknown, fromStep: unknown): 'data' | 'accept' | undefined {
for (const value of [fromStep, fromCard]) {
if (value === 'data' || value === 'accept') return value;
}
return undefined;
}
export function decisionCardFace(
input: { readonly variant?: 'review' | undefined; readonly checklist?: ReadonlyArray<string> | undefined },
step: { readonly cardVariant?: string | undefined; readonly acceptanceChecklist?: ReadonlyArray<string> | undefined } | undefined,
@@ -1913,8 +1934,7 @@ export function wireEngines(deps: EngineWiringDeps): EngineWiring {
: (await deps.registry.manifestOf((await taskContext.of(input.taskId)).activityId)).assets.businessModel.steps?.find(
(s) => s.item === input.itemId,
)?.cardVariant;
// 执行工单这一路只认 data(review 是决策卡那一路的面孔)
const variant = stepVariant === 'data' || input.card['variant'] === 'data' ? ('data' as const) : undefined;
const variant = executionCardVariant(input.card['variant'], stepVariant);
const issued = await deps.humanTool.issueExecution(context, {
title,
basis: basis.length > 0 ? basis : [{ k: '来源', v: '案例模型当下局面' }],