Archived
feat(w05): 差异评估案例模型 —— CMMN 八项七哨兵、说明书五卡骨架、八条剧本、五段进度
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FJcX8A6TWp4GRs41TYryEh
This commit is contained in:
@@ -11,6 +11,8 @@ import { activityManifestSchema } from '@contracts';
|
||||
import { manifest, D, RULE_VERSION } from '../src/manifest.ts';
|
||||
import activity from '../src/index.ts';
|
||||
|
||||
const ITEMS = ['S0_bind', 'S0d_master_data', 'S1_input_confirm', 'S2_normalize', 'S3_assess', 'S4_review', 'S5_prepare_landing', 'T_release'];
|
||||
const NO_DONE_WHEN = new Set(['S0d_master_data', 'S1_input_confirm', 'S4_review', 'T_release']);
|
||||
const VIEWS = ['p08-master-create', 'p10-document-create', 'eval-input-list', 'difference-review', 'landing-confirm'];
|
||||
const byAddress = (domain: string, object: string) => manifest.declarations.ledger.find((l) => l.domain === domain && l.object === object);
|
||||
|
||||
@@ -121,6 +123,60 @@ describe('台账声明:授权面与《统一方案》口径', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('案例模型与清单的对齐', () => {
|
||||
it('八个计划项,剧本 item 与 CMMN humanTask id 一一对应', () => {
|
||||
const cmmnIds = [...manifest.assets.businessModel.cmmnXml.matchAll(/<humanTask id="([^"]+)"/g)].map((m) => m[1] ?? '');
|
||||
expect(cmmnIds).toEqual(ITEMS);
|
||||
expect((manifest.assets.businessModel.steps as ReadonlyArray<{ item: string }>).map((s) => s.item)).toEqual(ITEMS);
|
||||
});
|
||||
|
||||
it('四处不给 doneWhen(数据工单 / 输入确认 / 审核 / 放行),其余四处都给', () => {
|
||||
for (const s of manifest.assets.businessModel.steps as ReadonlyArray<{ item: string; doneWhen?: string }>) {
|
||||
if (NO_DONE_WHEN.has(s.item)) expect(s.doneWhen, s.item).toBeUndefined();
|
||||
else expect(s.doneWhen, s.item).toBeDefined();
|
||||
}
|
||||
});
|
||||
|
||||
it('那四项在 CMMN 里都标了人工激活', () => {
|
||||
for (const id of NO_DONE_WHEN) {
|
||||
const block = new RegExp(`<humanTask id="${id}"[\\s\\S]*?</humanTask>`).exec(manifest.assets.businessModel.cmmnXml)?.[0] ?? '';
|
||||
expect(block, id).toContain('manualActivationRule');
|
||||
}
|
||||
});
|
||||
|
||||
it('除放行外每一项恰好属于一个阶段;5 段 + 放行 = x/6,与文档 0.5/1/2/3/4/5 六步对齐', () => {
|
||||
const phases = manifest.assets.businessModel.phases ?? [];
|
||||
expect(phases).toHaveLength(5);
|
||||
const phaseItems = phases.flatMap((p) => p.items);
|
||||
expect([...phaseItems].sort()).toEqual([...ITEMS.filter((i) => i !== 'T_release')].sort());
|
||||
expect(new Set(phaseItems).size).toBe(phaseItems.length);
|
||||
});
|
||||
|
||||
it('说明书点名的计划项都真实存在', () => {
|
||||
const mentioned = [...manifest.assets.businessModel.narrative.matchAll(/\b(S\d[a-z]?_[a-zA-Z_]+|T_release)\b/g)].map((m) => m[1] ?? '');
|
||||
for (const name of mentioned) expect(ITEMS, `说明书提到但不存在的计划项: ${name}`).toContain(name);
|
||||
});
|
||||
|
||||
it('两种决策卡骨架都在说明书里:候选放弃/创建(create/abandon)与审核(approve/reject/custom:)', () => {
|
||||
const n = manifest.assets.businessModel.narrative;
|
||||
expect(n).toContain('"kind":"decide","itemId":"S0d_master_data"');
|
||||
expect(n).toContain('"value":"create"');
|
||||
expect(n).toContain('"value":"abandon"');
|
||||
expect(n).toContain('"kind":"decide","itemId":"S4_review"');
|
||||
expect(n).toContain('"approve"');
|
||||
expect(n).toContain('"reject"');
|
||||
expect(n).toContain('"custom:"');
|
||||
});
|
||||
|
||||
it('T_release 剧本里 judge criterion 的原文逐字对得上 acceptanceCriteria 的 label', () => {
|
||||
const tRelease = (manifest.assets.businessModel.steps as ReadonlyArray<{ item: string; guide?: string }>).find((s) => s.item === 'T_release');
|
||||
const quoted = [...(tRelease?.guide ?? '').matchAll(/judge criterion="([^"]+)"/g)].map((m) => m[1] ?? '');
|
||||
expect(quoted).toHaveLength(3);
|
||||
const labels = new Set(manifest.contract.acceptanceCriteria.map((c) => c.label));
|
||||
for (const q of quoted) expect(labels.has(q), q).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('活动入口', () => {
|
||||
it('没有 run —— 行为全在清单里', () => {
|
||||
expect(activity.run).toBeUndefined();
|
||||
|
||||
Reference in New Issue
Block a user