MVP底座③:六活动槽位清单 —— 契约就绪,实现留白,工具按名解耦

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
This commit is contained in:
2026-07-27 22:45:32 +08:00
co-authored by Claude Fable 5
parent d00e29091e
commit 90ca92aa0e
2 changed files with 534 additions and 0 deletions
@@ -0,0 +1,44 @@
/**
* 六个活动槽位 —— 契约就绪、实现留白。守:范式/岗位/出口与 MVP 一致;
* 声明只有名(工具按名引用、域对象按名取)—— 活动与工具的解耦就靠这一条。
* A5 定价审批是**内置审批点**,零开发、不入库 —— 不在槽位之列。
*/
import { describe, expect, it } from 'vitest';
import { MVP_SLOTS, SLOT_ARTIFACT } from '../seed-mvp-slots.ts';
describe('MVP_SLOTS', () => {
it('六槽位,id 与范式如 MVP(A5 审批点内置,不入库)', () => {
const byId = new Map(MVP_SLOTS.map((m) => [m.identity.activityId as string, m.identity.paradigm]));
expect(MVP_SLOTS).toHaveLength(6);
expect(byId.get('rfq-intake')).toBe('flow');
expect(byId.get('alt-selection')).toBe('loop');
expect(byId.get('sourcing-inquiry')).toBe('loop');
expect(byId.get('cost-pricing')).toBe('flow');
expect(byId.has('pricing-approval')).toBe(false);
expect(byId.get('quote-negotiation')).toBe('loop');
expect(byId.get('intel-archive')).toBe('flow');
});
it('A1 出口带两枚去向旗;A2 透传 needSourcing —— 网关条件的数据来源', () => {
const a1 = MVP_SLOTS.find((m) => m.identity.activityId === 'rfq-intake')!;
const p1 = (a1.contract.output.mandatory[0]!.payload as { properties?: Record<string, unknown> }).properties ?? {};
expect(Object.keys(p1)).toEqual(expect.arrayContaining(['needAlternative', 'needSourcing']));
const a2 = MVP_SLOTS.find((m) => m.identity.activityId === 'alt-selection')!;
const p2 = (a2.contract.output.mandatory[0]!.payload as { properties?: Record<string, unknown> }).properties ?? {};
expect(Object.keys(p2)).toContain('needSourcing');
});
it('A6 出口带 outcome 三值 —— G4 议价结果的判据', () => {
const a6 = MVP_SLOTS.find((m) => m.identity.activityId === 'quote-negotiation')!;
const p6 = (a6.contract.output.mandatory[0]!.payload as { properties?: Record<string, Record<string, unknown>> })
.properties ?? {};
expect(p6['outcome']?.['enum']).toEqual(['won', 'revise', 'lost']);
});
it('槽位不携实现:入口是抛「未实现」的占位,工具声明只有名', () => {
expect(SLOT_ARTIFACT).toContain('未实现');
for (const m of MVP_SLOTS) {
for (const t of m.declarations.tools) expect(typeof t.name).toBe('string');
}
});
});