MVP底座③:六活动槽位清单 —— 契约就绪,实现留白,工具按名解耦
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
This commit is contained in:
@@ -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');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,490 @@
|
|||||||
|
/**
|
||||||
|
* 六个活动槽位(docs/MVP示例工作流.html §1.1–1.7)—— **契约就绪、实现留白**。
|
||||||
|
*
|
||||||
|
* 槽位 = 活动的契约面:身份 / 七件契约(入参、出口)/ 声明(域对象、工具名)/ 业务模型骨架。
|
||||||
|
* 与工具的解耦就在声明:`declarations.tools` 只写**名**,注册、连接值、白名单全在治理侧。
|
||||||
|
* A5 定价审批是**内置审批点**(审批不是任务,零开发、不入库),不在槽位之列。
|
||||||
|
*
|
||||||
|
* 实现待底座完备后逐个开发:届时以新包版本经安装通道换入,槽位契约不动。
|
||||||
|
*/
|
||||||
|
import { createHash } from 'node:crypto';
|
||||||
|
import { activityManifestSchema } from '@contracts';
|
||||||
|
import type { ActivityManifest } from '@contracts';
|
||||||
|
import type { Container } from './container.ts';
|
||||||
|
import { declarationsFor } from './seed-mvp-data.ts';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 槽位占位入口 —— **不是实现**:装载它只会得到「未实现」的诚实报错。
|
||||||
|
* mock 只许在外界:这里绝不发既定载荷装作跑过(README 平移规则)。
|
||||||
|
*/
|
||||||
|
export const SLOT_ARTIFACT = [
|
||||||
|
'// 活动槽位:契约已就绪,实现待底座完备后逐个开发。',
|
||||||
|
"throw new Error('活动尚未实现 —— 这是槽位,契约见治理台活动库');",
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
const checksum = createHash('sha256').update(SLOT_ARTIFACT).digest('hex');
|
||||||
|
|
||||||
|
/** flow 槽位的业务模型骨架:MVP 作业指导书的步骤标题 —— 未实现期一步一张执行工单,人工可兜 */
|
||||||
|
const flowSkeleton = (steps: readonly string[]) => ({
|
||||||
|
paradigm: 'flow' as const,
|
||||||
|
steps: steps.map((label, i) => ({
|
||||||
|
name: `S${i + 1}`,
|
||||||
|
label,
|
||||||
|
input: {},
|
||||||
|
kind: 'human' as const,
|
||||||
|
workOrder: 'execution' as const,
|
||||||
|
card: {},
|
||||||
|
deadline: 'P1D',
|
||||||
|
// 失败策略声明给定,不临场:人步失败即受阻待人 —— 槽位期没有可重试的机器步
|
||||||
|
failure: { retry: { times: 0, backoff: 'PT30S' }, fallback: { kind: 'blocked' as const } },
|
||||||
|
})),
|
||||||
|
parallelGroups: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
/** loop 槽位:CMMN 骨架(MVP 案例图的项)+ 叙事一句话;DMN 待实现期再挂 */
|
||||||
|
const loopSkeleton = (items: readonly string[], narrative: string) => ({
|
||||||
|
paradigm: 'loop' as const,
|
||||||
|
cmmnXml: `<case><casePlanModel autoComplete="true">${items.map((it) => `<task name="${it}"/>`).join('')}</casePlanModel></case>`,
|
||||||
|
narrative,
|
||||||
|
dmn: { drd: [], tables: [] },
|
||||||
|
});
|
||||||
|
|
||||||
|
const identity = (activityId: string, name: string, paradigm: 'flow' | 'loop') => ({
|
||||||
|
activityId,
|
||||||
|
name,
|
||||||
|
packageVersion: '0.1.0',
|
||||||
|
category: '统一报价',
|
||||||
|
paradigm,
|
||||||
|
sdkVersion: '0.1.0-m5',
|
||||||
|
checksum,
|
||||||
|
});
|
||||||
|
|
||||||
|
const harness = { memoryWindow: 20, stallAfter: 'PT45M', nudge: [] };
|
||||||
|
|
||||||
|
/** 七件契约里逐槽位相同的空段 —— 槽位期没有必交付物/验收细则,实现期再补 */
|
||||||
|
const contractRest = { requiredDeliverables: [], acceptanceCriteria: [], prohibitions: [] };
|
||||||
|
|
||||||
|
const budget = { steps: 40, tokens: 200_000, activeMinutes: 60, humanCalls: 3 };
|
||||||
|
|
||||||
|
export const MVP_SLOTS: readonly ActivityManifest[] = [
|
||||||
|
/* ── A1 客户RFQ受理(SOP · BD) ──────────────────────────────────── */
|
||||||
|
activityManifestSchema.parse({
|
||||||
|
identity: identity('rfq-intake', '客户RFQ受理', 'flow'),
|
||||||
|
contract: {
|
||||||
|
activityId: 'rfq-intake',
|
||||||
|
version: '0.1.0',
|
||||||
|
input: {
|
||||||
|
data: {
|
||||||
|
type: 'object',
|
||||||
|
required: ['customerCode', 'rfqRef'],
|
||||||
|
properties: {
|
||||||
|
customerCode: { type: 'string' },
|
||||||
|
rfqRef: { type: 'string' },
|
||||||
|
subject: { type: 'string' },
|
||||||
|
body: { type: 'string' },
|
||||||
|
quotedAt: { type: 'string' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
messageAllowed: true,
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
mandatory: [
|
||||||
|
{
|
||||||
|
name: 'intake',
|
||||||
|
description: '统一受理记录(打标)+ 两枚去向旗',
|
||||||
|
payload: {
|
||||||
|
type: 'object',
|
||||||
|
required: ['records', 'needAlternative', 'needSourcing'],
|
||||||
|
properties: {
|
||||||
|
records: { type: 'array' },
|
||||||
|
pn: { type: 'string' },
|
||||||
|
type_tag: { type: 'string' },
|
||||||
|
needAlternative: { type: 'boolean' },
|
||||||
|
needSourcing: { type: 'boolean' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
selective: [],
|
||||||
|
},
|
||||||
|
...contractRest,
|
||||||
|
authorization: { discretion: [], preauthorizedActions: [], humanCallBudget: 3 },
|
||||||
|
resources: { tools: [{ name: 'crm.history-price-read' }], skills: [], models: [], roles: ['BD'], budget },
|
||||||
|
},
|
||||||
|
declarations: {
|
||||||
|
models: [],
|
||||||
|
listeners: [],
|
||||||
|
harness,
|
||||||
|
tools: [{ name: 'crm.history-price-read' }],
|
||||||
|
ledger: declarationsFor([
|
||||||
|
'project.rfq_file',
|
||||||
|
'project.intake_record',
|
||||||
|
'project.pn_class',
|
||||||
|
'project.alt_request',
|
||||||
|
'project.inquiry_request',
|
||||||
|
'intel.history_price',
|
||||||
|
'customer.profile',
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
assets: {
|
||||||
|
businessModel: flowSkeleton(['接收登记', '追问补全信息', '逐条解析归一', '查历史价', '逐条定状况标', '定去向与派单']),
|
||||||
|
skills: [],
|
||||||
|
viewTemplates: [],
|
||||||
|
cardSchemas: {},
|
||||||
|
distillHooks: [],
|
||||||
|
},
|
||||||
|
samples: [
|
||||||
|
{
|
||||||
|
name: '契约样例:受理一单',
|
||||||
|
input: { customerCode: 'C001', rfqRef: 'RFQ-001' },
|
||||||
|
expect: { ports: ['intake'] },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
entry: { file: 'index.js', checksum },
|
||||||
|
}),
|
||||||
|
|
||||||
|
/* ── A2 替代选型(Case · FAE) ──────────────────────────────────── */
|
||||||
|
activityManifestSchema.parse({
|
||||||
|
identity: identity('alt-selection', '替代选型', 'loop'),
|
||||||
|
contract: {
|
||||||
|
activityId: 'alt-selection',
|
||||||
|
version: '0.1.0',
|
||||||
|
input: {
|
||||||
|
data: {
|
||||||
|
type: 'object',
|
||||||
|
required: ['material'],
|
||||||
|
properties: {
|
||||||
|
material: { type: 'string' },
|
||||||
|
chip_type: { type: 'string' },
|
||||||
|
spec_params: { type: 'string' },
|
||||||
|
scenario: { type: 'string' },
|
||||||
|
needSourcing: { type: 'boolean' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
messageAllowed: true,
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
mandatory: [
|
||||||
|
{
|
||||||
|
name: 'benchmark',
|
||||||
|
description: '替代对标表(已签字)+ needSourcing 原样带出(G2 在本路径读它)',
|
||||||
|
payload: {
|
||||||
|
type: 'object',
|
||||||
|
required: ['rows', 'needSourcing'],
|
||||||
|
properties: { rows: { type: 'array' }, needSourcing: { type: 'boolean' } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
selective: [],
|
||||||
|
},
|
||||||
|
...contractRest,
|
||||||
|
authorization: { discretion: [], preauthorizedActions: [], humanCallBudget: 3 },
|
||||||
|
resources: { tools: [{ name: 'crm.history-price-read' }], skills: [], models: [], roles: ['FAE'], budget },
|
||||||
|
},
|
||||||
|
declarations: {
|
||||||
|
models: [],
|
||||||
|
listeners: [],
|
||||||
|
harness,
|
||||||
|
tools: [{ name: 'crm.history-price-read' }],
|
||||||
|
ledger: declarationsFor(['intel.chip_spec', 'intel.public_select', 'project.alt_benchmark']),
|
||||||
|
},
|
||||||
|
assets: {
|
||||||
|
businessModel: loopSkeleton(
|
||||||
|
['替代对标表', '初筛清单'],
|
||||||
|
'MVP §1.5:上接 G1 是否需要选型;齐了即收 —— 必做项完成且无进行中项即结案,办结经 G2 定去向。',
|
||||||
|
),
|
||||||
|
skills: [],
|
||||||
|
viewTemplates: [],
|
||||||
|
cardSchemas: {},
|
||||||
|
distillHooks: [],
|
||||||
|
},
|
||||||
|
samples: [
|
||||||
|
{
|
||||||
|
name: '契约样例:一件替代评估',
|
||||||
|
input: { material: 'LM358DR' },
|
||||||
|
expect: { ports: ['benchmark'] },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
entry: { file: 'index.js', checksum },
|
||||||
|
}),
|
||||||
|
|
||||||
|
/* ── A3 寻源询价(Case · PRM) ──────────────────────────────────── */
|
||||||
|
activityManifestSchema.parse({
|
||||||
|
identity: identity('sourcing-inquiry', '寻源询价', 'loop'),
|
||||||
|
contract: {
|
||||||
|
activityId: 'sourcing-inquiry',
|
||||||
|
version: '0.1.0',
|
||||||
|
input: {
|
||||||
|
data: {
|
||||||
|
type: 'object',
|
||||||
|
required: ['pn'],
|
||||||
|
properties: {
|
||||||
|
pn: { type: 'string' },
|
||||||
|
brand: { type: 'string' },
|
||||||
|
era: { type: 'string' },
|
||||||
|
project_stage: { type: 'string' },
|
||||||
|
accept_alt: { type: 'boolean' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
messageAllowed: true,
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
mandatory: [
|
||||||
|
{
|
||||||
|
name: 'compare',
|
||||||
|
description: '寻源比价表 + 供应商底价表',
|
||||||
|
payload: {
|
||||||
|
type: 'object',
|
||||||
|
required: ['rows', 'floors'],
|
||||||
|
properties: { rows: { type: 'array' }, floors: { type: 'array' } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
selective: [],
|
||||||
|
},
|
||||||
|
...contractRest,
|
||||||
|
authorization: { discretion: [], preauthorizedActions: [], humanCallBudget: 3 },
|
||||||
|
resources: {
|
||||||
|
tools: [{ name: 'crm.history-price-read' }, { name: 'mail.send' }],
|
||||||
|
skills: [],
|
||||||
|
models: [],
|
||||||
|
roles: ['PRM'],
|
||||||
|
budget,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
declarations: {
|
||||||
|
models: [],
|
||||||
|
listeners: [],
|
||||||
|
harness,
|
||||||
|
tools: [{ name: 'crm.history-price-read' }, { name: 'mail.send' }],
|
||||||
|
ledger: declarationsFor([
|
||||||
|
'supplier.channel_dir',
|
||||||
|
'supplier.dead_stock',
|
||||||
|
'project.sourcing_compare',
|
||||||
|
'project.supplier_floor',
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
assets: {
|
||||||
|
businessModel: loopSkeleton(
|
||||||
|
['寻源比价表', '供应商回价单'],
|
||||||
|
'MVP §1.7:上接 G2 是否需要寻源;齐了即收 —— 必做项完成且无进行中项即结案,办结转 A4 成本定价。',
|
||||||
|
),
|
||||||
|
skills: [],
|
||||||
|
viewTemplates: [],
|
||||||
|
cardSchemas: {},
|
||||||
|
distillHooks: [],
|
||||||
|
},
|
||||||
|
samples: [
|
||||||
|
{
|
||||||
|
name: '契约样例:一单询价',
|
||||||
|
input: { pn: 'LM358DR' },
|
||||||
|
expect: { ports: ['compare'] },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
entry: { file: 'index.js', checksum },
|
||||||
|
}),
|
||||||
|
|
||||||
|
/* ── A4 成本定价(SOP · BD) ────────────────────────────────────── */
|
||||||
|
activityManifestSchema.parse({
|
||||||
|
identity: identity('cost-pricing', '成本定价', 'flow'),
|
||||||
|
contract: {
|
||||||
|
activityId: 'cost-pricing',
|
||||||
|
version: '0.1.0',
|
||||||
|
input: {
|
||||||
|
data: {
|
||||||
|
type: 'object',
|
||||||
|
required: ['records'],
|
||||||
|
properties: {
|
||||||
|
records: { type: 'array' },
|
||||||
|
floors: { type: 'array' },
|
||||||
|
benchmark: { type: 'array' },
|
||||||
|
customerCode: { type: 'string' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
messageAllowed: true,
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
mandatory: [
|
||||||
|
{
|
||||||
|
name: 'draft',
|
||||||
|
description: '报价草案:逐条定价,先过底价门禁再提审',
|
||||||
|
payload: {
|
||||||
|
type: 'object',
|
||||||
|
required: ['rows'],
|
||||||
|
properties: { rows: { type: 'array' }, quote_no: { type: 'string' } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
selective: [],
|
||||||
|
},
|
||||||
|
...contractRest,
|
||||||
|
authorization: { discretion: [], preauthorizedActions: [], humanCallBudget: 3 },
|
||||||
|
resources: { tools: [{ name: 'crm.history-price-read' }], skills: [], models: [], roles: ['BD'], budget },
|
||||||
|
},
|
||||||
|
declarations: {
|
||||||
|
models: [],
|
||||||
|
listeners: [],
|
||||||
|
harness,
|
||||||
|
tools: [{ name: 'crm.history-price-read' }],
|
||||||
|
ledger: declarationsFor(['intel.history_price', 'customer.profile', 'project.quote_draft']),
|
||||||
|
},
|
||||||
|
assets: {
|
||||||
|
businessModel: flowSkeleton(['取价基准', '逐条定价']),
|
||||||
|
skills: [],
|
||||||
|
viewTemplates: [],
|
||||||
|
cardSchemas: {},
|
||||||
|
distillHooks: [],
|
||||||
|
},
|
||||||
|
samples: [
|
||||||
|
{
|
||||||
|
name: '契约样例:一批草案',
|
||||||
|
input: { records: [] },
|
||||||
|
expect: { ports: ['draft'] },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
entry: { file: 'index.js', checksum },
|
||||||
|
}),
|
||||||
|
|
||||||
|
/* ── A6 报价议价(Case · BD) ───────────────────────────────────── */
|
||||||
|
activityManifestSchema.parse({
|
||||||
|
identity: identity('quote-negotiation', '报价议价', 'loop'),
|
||||||
|
contract: {
|
||||||
|
activityId: 'quote-negotiation',
|
||||||
|
version: '0.1.0',
|
||||||
|
input: {
|
||||||
|
data: {
|
||||||
|
type: 'object',
|
||||||
|
required: ['rows'],
|
||||||
|
properties: { rows: { type: 'array' }, quote_no: { type: 'string' }, customerCode: { type: 'string' } },
|
||||||
|
},
|
||||||
|
messageAllowed: true,
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
mandatory: [
|
||||||
|
{
|
||||||
|
name: 'formal',
|
||||||
|
description: '正式报价单(已送出)+ 跟进提醒 + 议价结果',
|
||||||
|
payload: {
|
||||||
|
type: 'object',
|
||||||
|
required: ['outcome'],
|
||||||
|
properties: {
|
||||||
|
outcome: { type: 'string', enum: ['won', 'revise', 'lost'] },
|
||||||
|
quote_no: { type: 'string' },
|
||||||
|
customer_code: { type: 'string' },
|
||||||
|
rows: { type: 'array' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
selective: [],
|
||||||
|
},
|
||||||
|
...contractRest,
|
||||||
|
authorization: { discretion: [], preauthorizedActions: [], humanCallBudget: 3 },
|
||||||
|
resources: {
|
||||||
|
tools: [{ name: 'crm.history-price-read' }, { name: 'mail.send' }],
|
||||||
|
skills: [],
|
||||||
|
models: [],
|
||||||
|
roles: ['BD'],
|
||||||
|
budget,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
declarations: {
|
||||||
|
models: [],
|
||||||
|
listeners: [],
|
||||||
|
harness,
|
||||||
|
tools: [{ name: 'crm.history-price-read' }, { name: 'mail.send' }],
|
||||||
|
ledger: declarationsFor(['project.formal_quote', 'project.followup']),
|
||||||
|
},
|
||||||
|
assets: {
|
||||||
|
businessModel: loopSkeleton(
|
||||||
|
['正式报价单'],
|
||||||
|
'MVP §1.4:上接审批通过进入;发出标准报价并跟谈,办结经 G4 议价结果定去向(成交 / 有异议 / 拒绝或超时)。',
|
||||||
|
),
|
||||||
|
skills: [],
|
||||||
|
viewTemplates: [],
|
||||||
|
cardSchemas: {},
|
||||||
|
distillHooks: [],
|
||||||
|
},
|
||||||
|
samples: [
|
||||||
|
{
|
||||||
|
name: '契约样例:一单议价',
|
||||||
|
input: { rows: [] },
|
||||||
|
expect: { ports: ['formal'] },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
entry: { file: 'index.js', checksum },
|
||||||
|
}),
|
||||||
|
|
||||||
|
/* ── A7 情报归档(SOP · BD) ────────────────────────────────────── */
|
||||||
|
activityManifestSchema.parse({
|
||||||
|
identity: identity('intel-archive', '情报归档', 'flow'),
|
||||||
|
contract: {
|
||||||
|
activityId: 'intel-archive',
|
||||||
|
version: '0.1.0',
|
||||||
|
input: {
|
||||||
|
data: {
|
||||||
|
type: 'object',
|
||||||
|
required: ['outcome'],
|
||||||
|
properties: {
|
||||||
|
outcome: { type: 'string' },
|
||||||
|
quote_no: { type: 'string' },
|
||||||
|
customer_code: { type: 'string' },
|
||||||
|
quotedAt: { type: 'string' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
messageAllowed: true,
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
mandatory: [
|
||||||
|
{
|
||||||
|
name: 'stats',
|
||||||
|
description: '不成交也留资产:情报、客户画像、落地率',
|
||||||
|
payload: {
|
||||||
|
type: 'object',
|
||||||
|
required: ['landing_rate'],
|
||||||
|
properties: { landing_rate: { type: 'number' } },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
selective: [],
|
||||||
|
},
|
||||||
|
...contractRest,
|
||||||
|
authorization: { discretion: [], preauthorizedActions: [], humanCallBudget: 3 },
|
||||||
|
resources: { tools: [], skills: [], models: [], roles: ['BD'], budget },
|
||||||
|
},
|
||||||
|
declarations: {
|
||||||
|
models: [],
|
||||||
|
listeners: [],
|
||||||
|
harness,
|
||||||
|
tools: [],
|
||||||
|
ledger: declarationsFor(['intel.history_price', 'customer.profile', 'project.landing_stats']),
|
||||||
|
},
|
||||||
|
assets: {
|
||||||
|
businessModel: flowSkeleton(['结果登记', '画像更新', '落地率统计']),
|
||||||
|
skills: [],
|
||||||
|
viewTemplates: [],
|
||||||
|
cardSchemas: {},
|
||||||
|
distillHooks: [],
|
||||||
|
},
|
||||||
|
samples: [
|
||||||
|
{
|
||||||
|
name: '契约样例:一次归档',
|
||||||
|
input: { outcome: 'lost' },
|
||||||
|
expect: { ports: ['stats'] },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
entry: { file: 'index.js', checksum },
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
export async function installSlots(container: Container): Promise<void> {
|
||||||
|
for (const manifest of MVP_SLOTS) {
|
||||||
|
const verdict = await container.registry.install(manifest, SLOT_ARTIFACT);
|
||||||
|
const errors = verdict.staticFindings.filter((finding) => finding.severity === 'error');
|
||||||
|
process.stdout.write(
|
||||||
|
` 槽位 ${manifest.identity.name}(${manifest.identity.paradigm})静态审 ${verdict.staticFindings.length} 条` +
|
||||||
|
(errors.length > 0 ? ` —— ERROR: ${errors.map((e) => e.message).join('; ')}` : '') +
|
||||||
|
'\n',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user