Archived
数据工单由剧本声明、引擎决定 —— 不靠模型抄卡骨架(W05 1.4.3)
用户:「右边 AI 聊天框里这个数据工单怎么又显示的执行工单?」查实:说明书骨架里 `"variant":"data"` 与 `gateLedgers` 并排写着,模型抄卡时 gateLedgers 抄到了、variant 丢了 (看着像元数据)。靠模型抄一个标记本来就靠不住。 改成与 `view` 同一条道理:这一步派的是什么单,写剧本时就定死 —— - 契约 `PlanStep.cardVariant: 'data'`;活动在 S0d_p08_master / S0d_p10_document 两步上声明; - `cards.issue` 入参多一项可选 `itemId`(turn-runner 从 human 动作带下来;引擎自发的验收/兜底卡没有项); - 组装层按 itemId 查剧本,`stepVariant ?? 卡上写的` —— 模型写不写都一样。 在跑那一单的卡已就地补上 variant(payload),刷新即见「数据工单」。 2102 测 / typecheck / lint / depcruise 绿;api-report 已刷;两处 CONTRACT.md 同步。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FJcX8A6TWp4GRs41TYryEh
This commit is contained in:
@@ -142,6 +142,11 @@ describe('说明书与剧本的骨架', () => {
|
|||||||
expect(NARRATIVE).not.toContain('"kind":"decide","itemId":"S0d_p08_master"');
|
expect(NARRATIVE).not.toContain('"kind":"decide","itemId":"S0d_p08_master"');
|
||||||
// 两张数据工单都得标 variant:"data" —— 对话区凭它换成「数据工单」那副面孔
|
// 两张数据工单都得标 variant:"data" —— 对话区凭它换成「数据工单」那副面孔
|
||||||
expect(NARRATIVE.match(/"variant":"data"/g)?.length).toBe(2);
|
expect(NARRATIVE.match(/"variant":"data"/g)?.length).toBe(2);
|
||||||
|
// 但模型抄卡会把它丢掉(2026-08-27 实测)—— 真正作数的是剧本上的声明,引擎按它决定
|
||||||
|
for (const item of ['S0d_p08_master', 'S0d_p10_document']) {
|
||||||
|
expect(ASSESSMENT_STEPS.find((s) => s.item === item)?.cardVariant, item).toBe('data');
|
||||||
|
}
|
||||||
|
for (const step of ASSESSMENT_STEPS) if (!['S0d_p08_master', 'S0d_p10_document'].includes(step.item)) expect(step.cardVariant, step.item).toBeUndefined();
|
||||||
expect(NARRATIVE).toContain('"title":"数据工单 · 芯片主数据创建(P-08)');
|
expect(NARRATIVE).toContain('"title":"数据工单 · 芯片主数据创建(P-08)');
|
||||||
expect(NARRATIVE).toContain('"title":"数据工单 · 原厂技术资料创建(P-10)');
|
expect(NARRATIVE).toContain('"title":"数据工单 · 原厂技术资料创建(P-10)');
|
||||||
expect(NARRATIVE).toContain('"title":"评估输入与资料补齐确认');
|
expect(NARRATIVE).toContain('"title":"评估输入与资料补齐确认');
|
||||||
|
|||||||
@@ -351,6 +351,8 @@ current_stage = "assessing"。application_scenario / bom_position **这一步不
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
item: 'S0d_p08_master',
|
item: 'S0d_p08_master',
|
||||||
|
/* 这一步派的是数据工单 —— 由剧本声明,模型抄不抄 variant 都一样(2026-08-27 实测它丢了) */
|
||||||
|
cardVariant: 'data' as const,
|
||||||
intent: '料号不在库:派 P-08 数据工单让人从零建芯片主数据 + 标准化参数子表 —— 查无记录一律派,不问人',
|
intent: '料号不在库:派 P-08 数据工单让人从零建芯片主数据 + 标准化参数子表 —— 查无记录一律派,不问人',
|
||||||
writes: ['parts.profile', 'parts.parameter', 'parts.document', `${D}.pn_check`],
|
writes: ['parts.profile', 'parts.parameter', 'parts.document', `${D}.pn_check`],
|
||||||
reads: [`${D}.pn_check`, 'candidateInternalPns', 'originalInternalPn', 'datasheets', 'parts.profile', 'parts.parameter', 'parts.document'],
|
reads: [`${D}.pn_check`, 'candidateInternalPns', 'originalInternalPn', 'datasheets', 'parts.profile', 'parts.parameter', 'parts.document'],
|
||||||
@@ -413,6 +415,8 @@ P-10 的资料表,一格都填不了,提交闸必然拦死。
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
item: 'S0d_p10_document',
|
item: 'S0d_p10_document',
|
||||||
|
/* 这一步派的是数据工单 —— 由剧本声明,模型抄不抄 variant 都一样(2026-08-27 实测它丢了) */
|
||||||
|
cardVariant: 'data' as const,
|
||||||
intent: '料号不在库:派 P-10 数据工单让人从零建原厂技术资料',
|
intent: '料号不在库:派 P-10 数据工单让人从零建原厂技术资料',
|
||||||
writes: ['parts.document', `${D}.pn_check`],
|
writes: ['parts.document', `${D}.pn_check`],
|
||||||
reads: [`${D}.pn_check`, 'parts.profile', 'parts.document'],
|
reads: [`${D}.pn_check`, 'parts.profile', 'parts.document'],
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ export const manifest: ActivityManifest = {
|
|||||||
identity: {
|
identity: {
|
||||||
activityId: ACTIVITY_ID,
|
activityId: ACTIVITY_ID,
|
||||||
name: 'ACT-WORK-05 · 差异评估',
|
name: 'ACT-WORK-05 · 差异评估',
|
||||||
packageVersion: '1.4.2',
|
packageVersion: '1.4.3',
|
||||||
category: '需求分析与方案匹配',
|
category: '需求分析与方案匹配',
|
||||||
sdkVersion: '0.1.0-m5',
|
sdkVersion: '0.1.0-m5',
|
||||||
checksum: 'dev-checksum-part-substitution-assessment',
|
checksum: 'dev-checksum-part-substitution-assessment',
|
||||||
|
|||||||
+17
@@ -48,6 +48,7 @@ export declare const manifestDeclarationsSchema: z.ZodObject<{
|
|||||||
derived: "derived";
|
derived: "derived";
|
||||||
}>>;
|
}>>;
|
||||||
caseKey: z.ZodOptional<z.ZodString>;
|
caseKey: z.ZodOptional<z.ZodString>;
|
||||||
|
createOnly: z.ZodOptional<z.ZodBoolean>;
|
||||||
file: z.ZodOptional<z.ZodObject<{
|
file: z.ZodOptional<z.ZodObject<{
|
||||||
titleColumn: z.ZodString;
|
titleColumn: z.ZodString;
|
||||||
bodyColumn: z.ZodString;
|
bodyColumn: z.ZodString;
|
||||||
@@ -201,6 +202,9 @@ export declare const planStepSchema: z.ZodObject<{
|
|||||||
view: z.ZodOptional<z.ZodString>;
|
view: z.ZodOptional<z.ZodString>;
|
||||||
tools: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
tools: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
||||||
doneWhen: z.ZodOptional<z.ZodString>;
|
doneWhen: z.ZodOptional<z.ZodString>;
|
||||||
|
cardVariant: z.ZodOptional<z.ZodEnum<{
|
||||||
|
data: "data";
|
||||||
|
}>>;
|
||||||
}, z.core.$strip>;
|
}, z.core.$strip>;
|
||||||
export type PlanStep = z.infer<typeof planStepSchema>;
|
export type PlanStep = z.infer<typeof planStepSchema>;
|
||||||
/**
|
/**
|
||||||
@@ -261,6 +265,9 @@ export declare const loopModelSchema: z.ZodObject<{
|
|||||||
view: z.ZodOptional<z.ZodString>;
|
view: z.ZodOptional<z.ZodString>;
|
||||||
tools: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
tools: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
||||||
doneWhen: z.ZodOptional<z.ZodString>;
|
doneWhen: z.ZodOptional<z.ZodString>;
|
||||||
|
cardVariant: z.ZodOptional<z.ZodEnum<{
|
||||||
|
data: "data";
|
||||||
|
}>>;
|
||||||
}, z.core.$strip>>>;
|
}, z.core.$strip>>>;
|
||||||
phases: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
phases: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
||||||
name: z.ZodString;
|
name: z.ZodString;
|
||||||
@@ -315,6 +322,9 @@ export declare const businessModelSchema: z.ZodObject<{
|
|||||||
view: z.ZodOptional<z.ZodString>;
|
view: z.ZodOptional<z.ZodString>;
|
||||||
tools: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
tools: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
||||||
doneWhen: z.ZodOptional<z.ZodString>;
|
doneWhen: z.ZodOptional<z.ZodString>;
|
||||||
|
cardVariant: z.ZodOptional<z.ZodEnum<{
|
||||||
|
data: "data";
|
||||||
|
}>>;
|
||||||
}, z.core.$strip>>>;
|
}, z.core.$strip>>>;
|
||||||
phases: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
phases: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
||||||
name: z.ZodString;
|
name: z.ZodString;
|
||||||
@@ -378,6 +388,9 @@ export declare const manifestAssetsSchema: z.ZodObject<{
|
|||||||
view: z.ZodOptional<z.ZodString>;
|
view: z.ZodOptional<z.ZodString>;
|
||||||
tools: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
tools: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
||||||
doneWhen: z.ZodOptional<z.ZodString>;
|
doneWhen: z.ZodOptional<z.ZodString>;
|
||||||
|
cardVariant: z.ZodOptional<z.ZodEnum<{
|
||||||
|
data: "data";
|
||||||
|
}>>;
|
||||||
}, z.core.$strip>>>;
|
}, z.core.$strip>>>;
|
||||||
phases: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
phases: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
||||||
name: z.ZodString;
|
name: z.ZodString;
|
||||||
@@ -537,6 +550,7 @@ export declare const activityManifestSchema: z.ZodObject<{
|
|||||||
derived: "derived";
|
derived: "derived";
|
||||||
}>>;
|
}>>;
|
||||||
caseKey: z.ZodOptional<z.ZodString>;
|
caseKey: z.ZodOptional<z.ZodString>;
|
||||||
|
createOnly: z.ZodOptional<z.ZodBoolean>;
|
||||||
file: z.ZodOptional<z.ZodObject<{
|
file: z.ZodOptional<z.ZodObject<{
|
||||||
titleColumn: z.ZodString;
|
titleColumn: z.ZodString;
|
||||||
bodyColumn: z.ZodString;
|
bodyColumn: z.ZodString;
|
||||||
@@ -651,6 +665,9 @@ export declare const activityManifestSchema: z.ZodObject<{
|
|||||||
view: z.ZodOptional<z.ZodString>;
|
view: z.ZodOptional<z.ZodString>;
|
||||||
tools: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
tools: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
||||||
doneWhen: z.ZodOptional<z.ZodString>;
|
doneWhen: z.ZodOptional<z.ZodString>;
|
||||||
|
cardVariant: z.ZodOptional<z.ZodEnum<{
|
||||||
|
data: "data";
|
||||||
|
}>>;
|
||||||
}, z.core.$strip>>>;
|
}, z.core.$strip>>>;
|
||||||
phases: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
phases: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
||||||
name: z.ZodString;
|
name: z.ZodString;
|
||||||
|
|||||||
@@ -1362,6 +1362,8 @@ export function wireEngines(deps: EngineWiringDeps): EngineWiring {
|
|||||||
ownerId: StaffId;
|
ownerId: StaffId;
|
||||||
humanCallCap: number;
|
humanCallCap: number;
|
||||||
workOrder: 'execution' | 'acceptance';
|
workOrder: 'execution' | 'acceptance';
|
||||||
|
/** 这张卡挂在哪个计划项上 —— 引擎按它查剧本声明(如 `cardVariant`),不靠模型抄卡骨架;引擎自己发的卡(验收/兜底)没有项 */
|
||||||
|
itemId?: string | undefined;
|
||||||
card: Record<string, unknown>;
|
card: Record<string, unknown>;
|
||||||
}) {
|
}) {
|
||||||
const context = { taskId: input.taskId, ownerId: input.ownerId, humanCallCap: input.humanCallCap };
|
const context = { taskId: input.taskId, ownerId: input.ownerId, humanCallCap: input.humanCallCap };
|
||||||
@@ -1730,8 +1732,19 @@ export function wireEngines(deps: EngineWiringDeps): EngineWiring {
|
|||||||
const gateLedgers = Array.isArray(input.card['gateLedgers'])
|
const gateLedgers = Array.isArray(input.card['gateLedgers'])
|
||||||
? input.card['gateLedgers'].map((x) => String(x).trim()).filter((x) => /^[a-z0-9_]+\.[a-z0-9_]+$/i.test(x))
|
? input.card['gateLedgers'].map((x) => String(x).trim()).filter((x) => /^[a-z0-9_]+\.[a-z0-9_]+$/i.test(x))
|
||||||
: [];
|
: [];
|
||||||
/* 数据工单 = 执行工单的变体(契约 `variant`):活动在卡上写 `"variant":"data"`,对话区据此换一副面孔 */
|
/*
|
||||||
const variant = input.card['variant'] === 'data' ? ('data' as const) : undefined;
|
* 数据工单 = 执行工单的变体(契约 `variant`)—— **由剧本声明、这里决定**(`PlanStep.cardVariant`),
|
||||||
|
* 不靠模型抄卡骨架:说明书里写着 `"variant":"data"`,模型抄卡时把它丢了(2026-08-27 实测,
|
||||||
|
* 同一张卡 gateLedgers 抄到了、variant 没有),对话区于是照旧摆「执行工单」。
|
||||||
|
* 与 `view` 同一条道理:这一步派的是什么单,写剧本时就定死,模型写不写都一样。卡上写了只当提示。
|
||||||
|
*/
|
||||||
|
const stepVariant =
|
||||||
|
input.itemId === undefined
|
||||||
|
? undefined
|
||||||
|
: (await deps.registry.manifestOf((await taskContext.of(input.taskId)).activityId)).assets.businessModel.steps?.find(
|
||||||
|
(s) => s.item === input.itemId,
|
||||||
|
)?.cardVariant;
|
||||||
|
const variant = stepVariant ?? (input.card['variant'] === 'data' ? ('data' as const) : undefined);
|
||||||
const issued = await deps.humanTool.issueExecution(context, {
|
const issued = await deps.humanTool.issueExecution(context, {
|
||||||
title,
|
title,
|
||||||
basis: basis.length > 0 ? basis : [{ k: '来源', v: '案例模型当下局面' }],
|
basis: basis.length > 0 ? basis : [{ k: '来源', v: '案例模型当下局面' }],
|
||||||
|
|||||||
@@ -127,3 +127,8 @@
|
|||||||
与「写的授权面是台账声明」「调工具的授权面是白名单」同一条 ——
|
与「写的授权面是台账声明」「调工具的授权面是白名单」同一条 ——
|
||||||
立了没人叫醒的监听不是等待,是失联(实测:模型在依赖断点上临场立 data 监听,
|
立了没人叫醒的监听不是等待,是失联(实测:模型在依赖断点上临场立 data 监听,
|
||||||
而那个活动一个监听都没声明,单子永久挂在 `awaiting-event`)
|
而那个活动一个监听都没声明,单子永久挂在 `awaiting-event`)
|
||||||
|
|
||||||
|
近变(2026-08-27):`cards.issue` 的入参多一项可选 `itemId`(这张卡挂在哪个计划项上)—— 组装层据此查剧本声明
|
||||||
|
(`PlanStep.cardVariant`:这一步派的是数据工单),**不靠模型抄卡骨架**(实测它会把 `variant` 抄丢)。
|
||||||
|
引擎自己发的卡(验收 / 兜底)没有项,不传。
|
||||||
|
|
||||||
|
|||||||
@@ -1126,6 +1126,7 @@ export function createTurnRunner(deps: TurnRunnerDeps): LoopEngineService {
|
|||||||
taskId,
|
taskId,
|
||||||
ownerId: context.ownerId,
|
ownerId: context.ownerId,
|
||||||
humanCallCap: context.humanCallCap,
|
humanCallCap: context.humanCallCap,
|
||||||
|
itemId: action.itemId,
|
||||||
workOrder: action.workOrder,
|
workOrder: action.workOrder,
|
||||||
card: action.card,
|
card: action.card,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -169,6 +169,8 @@ export type CardGateway = {
|
|||||||
ownerId: StaffId;
|
ownerId: StaffId;
|
||||||
humanCallCap: number;
|
humanCallCap: number;
|
||||||
workOrder: 'execution' | 'acceptance';
|
workOrder: 'execution' | 'acceptance';
|
||||||
|
/** 这张卡挂在哪个计划项上 —— 引擎按它查剧本声明(如 `cardVariant`),不靠模型抄卡骨架;引擎自己发的卡(验收/兜底)没有项 */
|
||||||
|
itemId?: string | undefined;
|
||||||
card: Record<string, unknown>;
|
card: Record<string, unknown>;
|
||||||
}): Promise<{ id: string }>;
|
}): Promise<{ id: string }>;
|
||||||
/**
|
/**
|
||||||
|
|||||||
+1
-1
@@ -252,7 +252,7 @@ activities/<id>/src/ ──(pnpm activities:build)──> dist/{manifest.json,in
|
|||||||
| `ask` | ~~已退役~~ | 基座原地转成零输入执行工单(字段名并进指导书);别再用 |
|
| `ask` | ~~已退役~~ | 基座原地转成零输入执行工单(字段名并进指导书);别再用 |
|
||||||
| `entry` | `{itemId, ask, ledger, entryKey, fields, also?}` | 数据录入卡,挂起;确认那一下基座直写 |
|
| `entry` | `{itemId, ask, ledger, entryKey, fields, also?}` | 数据录入卡,挂起;确认那一下基座直写 |
|
||||||
| `decide` | `{itemId, ask, options(≥2), basis}` | 决策卡,挂起 |
|
| `decide` | `{itemId, ask, options(≥2), basis}` | 决策卡,挂起 |
|
||||||
| `human` | `{itemId, workOrder, card{title,ask,steps?,standard?,basis}}` | 执行/验收工单,挂起。**执行卡零输入**(2026-08-02 用户定):不摆 `fields` 不绑 `blocks`,填写全在工作区的件上(editGate + 孪生件),人按「保存」基座直写草稿层;必填由提交闸看住 —— **卡上加 `gateLedgers:["域.对象"]` 告诉闸这一张要拦哪几张表**,不给才回落到必交付物声明。**数据工单**(让人从零建一张主数据表)在卡上再加 `variant:"data"` —— 引擎那一侧与执行工单无异,对话区据此换成「数据工单」那副面孔 |
|
| `human` | `{itemId, workOrder, card{title,ask,steps?,standard?,basis}}` | 执行/验收工单,挂起。**执行卡零输入**(2026-08-02 用户定):不摆 `fields` 不绑 `blocks`,填写全在工作区的件上(editGate + 孪生件),人按「保存」基座直写草稿层;必填由提交闸看住 —— **卡上加 `gateLedgers:["域.对象"]` 告诉闸这一张要拦哪几张表**,不给才回落到必交付物声明。**数据工单**(让人从零建一张主数据表)在那一步的剧本上声明 `cardVariant: 'data'` —— 引擎那一侧与执行工单无异,对话区据此换成「数据工单」那副面孔;卡骨架上的 `variant:"data"` 只是给模型看的提示,模型抄丢了也不影响(实测它会丢) |
|
||||||
| `listen` | `{itemId, eventType, keyFilter, deadline}` | 立监听,挂起;时限必配 |
|
| `listen` | `{itemId, eventType, keyFilter, deadline}` | 立监听,挂起;时限必配 |
|
||||||
| `finish` | `{itemId, rationale}` | 宣告一项完成;非人工激活的项就地过 Active 再收 |
|
| `finish` | `{itemId, rationale}` | 宣告一项完成;非人工激活的项就地过 Active 再收 |
|
||||||
| `submit` | `{rationale, fields?}` | 提交放行 → 预检 → 发验收卡挂起 |
|
| `submit` | `{rationale, fields?}` | 提交放行 → 预检 → 发验收卡挂起 |
|
||||||
|
|||||||
@@ -159,6 +159,14 @@ export const planStepSchema = z.object({
|
|||||||
* 该改判同一步写下的计数列。
|
* 该改判同一步写下的计数列。
|
||||||
*/
|
*/
|
||||||
doneWhen: z.string().optional(),
|
doneWhen: z.string().optional(),
|
||||||
|
/**
|
||||||
|
* **这一步派的执行工单是数据工单**(2026-08-27)—— 由剧本声明、引擎决定,不靠模型抄卡骨架。
|
||||||
|
*
|
||||||
|
* 起因:说明书的卡骨架里写着 `"variant":"data"`,模型抄卡时把它丢了(看着像元数据),
|
||||||
|
* 对话区于是照旧摆「执行工单」。与 `view` 同一条道理:这一步派的是什么单,是写剧本时就定死的事,
|
||||||
|
* 归活动声明、不归模型临场发挥;模型写不写都一样。
|
||||||
|
*/
|
||||||
|
cardVariant: z.enum(['data']).optional(),
|
||||||
});
|
});
|
||||||
export type PlanStep = z.infer<typeof planStepSchema>;
|
export type PlanStep = z.infer<typeof planStepSchema>;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user