Archived
feat(contracts): auto 配方契约 —— 确定的活由引擎按声明做,五个动词四种地址
配方是数据不是代码(ADR-004:不执行包代码即知包为何物),装进来可静态审、可回放。 只声明真实现了的那几式;嵌套两层封顶,没实现的形状 zod 当场拒。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LKMZDwMbyKzjw6Hembwvet
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
export {};
|
||||||
@@ -2238,6 +2238,7 @@ export declare const installActivityRoute: {
|
|||||||
}>>;
|
}>>;
|
||||||
acceptanceChecklist: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
acceptanceChecklist: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
||||||
humanWrites: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
humanWrites: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
||||||
|
auto: z.ZodOptional<z.ZodType<import("../auto-recipe.ts").AutoRecipe, unknown, z.core.$ZodTypeInternals<import("../auto-recipe.ts").AutoRecipe, unknown>>>;
|
||||||
}, 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;
|
||||||
|
|||||||
+135
@@ -0,0 +1,135 @@
|
|||||||
|
/**
|
||||||
|
* 确定性步骤的**声明式配方**(ADR-014 / spec 2026-08-29 §5.1)。
|
||||||
|
*
|
||||||
|
* 配方是**数据不是代码**:装进来仍然只是数据,可静态审、可回放、可在治理台上摆出
|
||||||
|
* 「这一步系统会做什么」。执行第三方代码则相反 —— ADR-004 的立场是「不执行包代码即知包为何物」。
|
||||||
|
*
|
||||||
|
* **只声明真实现了的那几式。** 表达力有上限是刻意的:上限之外的不要硬塞,
|
||||||
|
* 退回模型,或者把那一段判断从这一步里切出去。
|
||||||
|
*/
|
||||||
|
import { z } from 'zod';
|
||||||
|
/** 表达式 —— 递归,`z.lazy` 是唯一写法(zod 的自引用) */
|
||||||
|
export type AutoExpr = string | number | boolean
|
||||||
|
/** 入参 / 基座读数 / `let` 里算好的值 —— 三者同一个命名空间,`let` 优先 */
|
||||||
|
| {
|
||||||
|
readonly in: string;
|
||||||
|
}
|
||||||
|
/** 本行的列 */
|
||||||
|
| {
|
||||||
|
readonly col: string;
|
||||||
|
}
|
||||||
|
/** 外层那一行的列(两层嵌套时) */
|
||||||
|
| {
|
||||||
|
readonly outer: string;
|
||||||
|
}
|
||||||
|
/** 拼接,一律按字符串 */
|
||||||
|
| {
|
||||||
|
readonly cat: readonly AutoExpr[];
|
||||||
|
}
|
||||||
|
/** 只留数字(铸号:`case:openedAt` → `20260829215103`) */
|
||||||
|
| {
|
||||||
|
readonly fmt: AutoExpr;
|
||||||
|
readonly as: 'digits';
|
||||||
|
}
|
||||||
|
/** 查另一张表的一列;查不到给 `else`(缺省空串) */
|
||||||
|
| {
|
||||||
|
readonly lookup: string;
|
||||||
|
readonly by: Readonly<Record<string, AutoExpr>>;
|
||||||
|
readonly take: string;
|
||||||
|
readonly else?: AutoExpr | undefined;
|
||||||
|
}
|
||||||
|
/** S-FEEL 条件(可嵌套)—— 已算出的同行字段进它的作用域 */
|
||||||
|
| {
|
||||||
|
readonly when: string;
|
||||||
|
readonly then: AutoExpr;
|
||||||
|
readonly else: AutoExpr;
|
||||||
|
};
|
||||||
|
export declare const autoExprSchema: z.ZodType<AutoExpr>;
|
||||||
|
/**
|
||||||
|
* 数据源 —— 这一笔要对着几行办。
|
||||||
|
*
|
||||||
|
* 地址只有四种(spec §5.1):`域.对象`(**任务视图 = 正式表 + 本单草稿叠加**,默认该用的那一种)·
|
||||||
|
* `mine:域.对象`(只有本单草稿写下的行)· `in:入参数组` · `result:xxx`(本拍 analyze 的出参)。
|
||||||
|
* **没有「纯正式表」那一种** —— 要区分「本单写的」与「别人放行的」,`mine:` 就够了。
|
||||||
|
*/
|
||||||
|
export type AutoSource = {
|
||||||
|
readonly once: true;
|
||||||
|
} | {
|
||||||
|
readonly rows: readonly Readonly<Record<string, AutoExpr>>[];
|
||||||
|
} | {
|
||||||
|
readonly each: string;
|
||||||
|
readonly where?: Readonly<Record<string, AutoExpr>> | undefined;
|
||||||
|
/** 第二层,**到此为止** —— 三层以上就别 auto 了 */
|
||||||
|
readonly then?: {
|
||||||
|
readonly each: string;
|
||||||
|
readonly where?: Readonly<Record<string, AutoExpr>> | undefined;
|
||||||
|
} | undefined;
|
||||||
|
};
|
||||||
|
export declare const autoSourceSchema: z.ZodType<AutoSource>;
|
||||||
|
/** 五个动词 —— `when` 为假就跳过这一笔 */
|
||||||
|
export type AutoAction = {
|
||||||
|
readonly kind: 'project';
|
||||||
|
readonly when?: string | undefined;
|
||||||
|
/** 台账地址「域.对象」—— 必须在 `declarations.ledger` 里(静态审) */
|
||||||
|
readonly into: string;
|
||||||
|
/** 这一笔投影出来的行**就是这张表在本单草稿里的全部**,多出来的撤掉。只动草稿层 */
|
||||||
|
readonly prune?: boolean | undefined;
|
||||||
|
readonly from: AutoSource;
|
||||||
|
readonly fields: Readonly<Record<string, AutoExpr>>;
|
||||||
|
} | {
|
||||||
|
readonly kind: 'analyze';
|
||||||
|
readonly when?: string | undefined;
|
||||||
|
/** 活动声明过的模型名 */
|
||||||
|
readonly model: string;
|
||||||
|
/** 出参落进拍内作用域 `result:<into>`,供后面的 project 用 `each`/`lookup` 消费 */
|
||||||
|
readonly into: string;
|
||||||
|
/** 给模型的 system 段 —— 说清判什么、四态各是什么意思 */
|
||||||
|
readonly instruction: string;
|
||||||
|
/** 入参由声明组装:一行一条,`fields` 决定每条摆哪几个键 */
|
||||||
|
readonly input: {
|
||||||
|
readonly from: AutoSource;
|
||||||
|
readonly fields: Readonly<Record<string, AutoExpr>>;
|
||||||
|
};
|
||||||
|
/** 出参 JSON Schema —— 顶层必须是 object */
|
||||||
|
readonly output: Readonly<Record<string, unknown>>;
|
||||||
|
} | {
|
||||||
|
readonly kind: 'card';
|
||||||
|
readonly when?: string | undefined;
|
||||||
|
readonly as: 'execution' | 'decide';
|
||||||
|
readonly variant?: ('data' | 'review') | undefined;
|
||||||
|
readonly title: AutoExpr;
|
||||||
|
/** 决策卡问的那句话 */
|
||||||
|
readonly ask?: AutoExpr | undefined;
|
||||||
|
/** 作业指导书 —— 一条一句,静态文本 */
|
||||||
|
readonly guide?: readonly string[] | undefined;
|
||||||
|
readonly standard?: AutoExpr | undefined;
|
||||||
|
/** 调用必附上下文 —— **基座拒发 basis 为空的工单**,别指望兜底(它摆的是内部家具) */
|
||||||
|
readonly basis?: readonly {
|
||||||
|
readonly k: string;
|
||||||
|
readonly v: AutoExpr;
|
||||||
|
}[] | undefined;
|
||||||
|
/** 这张卡的提交闸盯哪几张表 */
|
||||||
|
readonly gateLedgers?: readonly string[] | undefined;
|
||||||
|
/** 决策卡的选项;`variant:'review'` 时允许只有一个 */
|
||||||
|
readonly options?: readonly {
|
||||||
|
readonly value: string;
|
||||||
|
readonly label: string;
|
||||||
|
readonly detail?: string | undefined;
|
||||||
|
}[] | undefined;
|
||||||
|
} | {
|
||||||
|
readonly kind: 'submit';
|
||||||
|
readonly when?: string | undefined;
|
||||||
|
readonly rationale: AutoExpr;
|
||||||
|
} | {
|
||||||
|
readonly kind: 'say';
|
||||||
|
readonly when?: string | undefined;
|
||||||
|
readonly text: AutoExpr;
|
||||||
|
};
|
||||||
|
export declare const autoActionSchema: z.ZodType<AutoAction>;
|
||||||
|
export type AutoRecipe = {
|
||||||
|
/** 配方级局部值:先算一遍,后面所有表达式都能 `{in:'名'}` 引用 —— 整单只有一个案号,靠它 */
|
||||||
|
readonly let?: Readonly<Record<string, AutoExpr>> | undefined;
|
||||||
|
/** 按序执行 */
|
||||||
|
readonly do: readonly AutoAction[];
|
||||||
|
};
|
||||||
|
export declare const autoRecipeSchema: z.ZodType<AutoRecipe>;
|
||||||
@@ -15,4 +15,5 @@ export * from './manifest.ts';
|
|||||||
export * from './org.ts';
|
export * from './org.ts';
|
||||||
export * from './mail.ts';
|
export * from './mail.ts';
|
||||||
export * from './activity-ops.ts';
|
export * from './activity-ops.ts';
|
||||||
|
export * from './auto-recipe.ts';
|
||||||
export * from './api/index.ts';
|
export * from './api/index.ts';
|
||||||
|
|||||||
@@ -208,6 +208,7 @@ export declare const planStepSchema: z.ZodObject<{
|
|||||||
}>>;
|
}>>;
|
||||||
acceptanceChecklist: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
acceptanceChecklist: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
||||||
humanWrites: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
humanWrites: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
||||||
|
auto: z.ZodOptional<z.ZodType<import("./auto-recipe.ts").AutoRecipe, unknown, z.core.$ZodTypeInternals<import("./auto-recipe.ts").AutoRecipe, unknown>>>;
|
||||||
}, z.core.$strip>;
|
}, z.core.$strip>;
|
||||||
export type PlanStep = z.infer<typeof planStepSchema>;
|
export type PlanStep = z.infer<typeof planStepSchema>;
|
||||||
/**
|
/**
|
||||||
@@ -274,6 +275,7 @@ export declare const loopModelSchema: z.ZodObject<{
|
|||||||
}>>;
|
}>>;
|
||||||
acceptanceChecklist: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
acceptanceChecklist: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
||||||
humanWrites: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
humanWrites: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
||||||
|
auto: z.ZodOptional<z.ZodType<import("./auto-recipe.ts").AutoRecipe, unknown, z.core.$ZodTypeInternals<import("./auto-recipe.ts").AutoRecipe, unknown>>>;
|
||||||
}, 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;
|
||||||
@@ -334,6 +336,7 @@ export declare const businessModelSchema: z.ZodObject<{
|
|||||||
}>>;
|
}>>;
|
||||||
acceptanceChecklist: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
acceptanceChecklist: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
||||||
humanWrites: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
humanWrites: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
||||||
|
auto: z.ZodOptional<z.ZodType<import("./auto-recipe.ts").AutoRecipe, unknown, z.core.$ZodTypeInternals<import("./auto-recipe.ts").AutoRecipe, unknown>>>;
|
||||||
}, 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;
|
||||||
@@ -492,6 +495,7 @@ export declare const manifestAssetsSchema: z.ZodObject<{
|
|||||||
}>>;
|
}>>;
|
||||||
acceptanceChecklist: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
acceptanceChecklist: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
||||||
humanWrites: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
humanWrites: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
||||||
|
auto: z.ZodOptional<z.ZodType<import("./auto-recipe.ts").AutoRecipe, unknown, z.core.$ZodTypeInternals<import("./auto-recipe.ts").AutoRecipe, unknown>>>;
|
||||||
}, 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;
|
||||||
@@ -798,6 +802,7 @@ export declare const activityManifestSchema: z.ZodObject<{
|
|||||||
}>>;
|
}>>;
|
||||||
acceptanceChecklist: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
acceptanceChecklist: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
||||||
humanWrites: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
humanWrites: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
||||||
|
auto: z.ZodOptional<z.ZodType<import("./auto-recipe.ts").AutoRecipe, unknown, z.core.$ZodTypeInternals<import("./auto-recipe.ts").AutoRecipe, unknown>>>;
|
||||||
}, 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;
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import { autoRecipeSchema } from '../auto-recipe.ts';
|
||||||
|
|
||||||
|
describe('auto 配方契约', () => {
|
||||||
|
it('收下一份用全了五个动词的配方', () => {
|
||||||
|
const parsed = autoRecipeSchema.safeParse({
|
||||||
|
let: { evalRef: { cat: ['W05-', { fmt: { in: 'case:openedAt' }, as: 'digits' }] } },
|
||||||
|
do: [
|
||||||
|
{ kind: 'project', into: 'sub_eval.part', prune: true, from: { rows: [{ pn: { in: 'originalInternalPn' } }] }, fields: { internal_pn: { col: 'pn' } } },
|
||||||
|
{ kind: 'analyze', model: 'deepseek-v4-flash', into: 'items', instruction: '逐条判四态', input: { from: { each: 'parts.type_param' }, fields: { name: { col: 'param_name' } } }, output: { type: 'object' } },
|
||||||
|
{ kind: 'card', as: 'execution', variant: 'data', title: '补数据', basis: [{ k: '原 PN', v: { in: 'originalInternalPn' } }], gateLedgers: ['parts.profile'] },
|
||||||
|
{ kind: 'submit', rationale: '结论已备好' },
|
||||||
|
{ kind: 'say', text: '这一步办完了' },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
expect(parsed.success).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('不认识的动词当场拒 —— 没实现的形状不许装进来', () => {
|
||||||
|
const parsed = autoRecipeSchema.safeParse({ do: [{ kind: 'drop', into: 'a.b' }] });
|
||||||
|
expect(parsed.success).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('嵌套只许两层:第三层当场拒', () => {
|
||||||
|
const parsed = autoRecipeSchema.safeParse({
|
||||||
|
do: [{ kind: 'project', into: 'a.b', from: { each: 'a.b', then: { each: 'c.d', then: { each: 'e.f' } } }, fields: {} }],
|
||||||
|
});
|
||||||
|
expect(parsed.success).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('prune 只许挂在 project 上', () => {
|
||||||
|
const parsed = autoRecipeSchema.safeParse({ do: [{ kind: 'say', text: 'x', prune: true }] });
|
||||||
|
expect(parsed.success).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,197 @@
|
|||||||
|
/**
|
||||||
|
* 确定性步骤的**声明式配方**(ADR-014 / spec 2026-08-29 §5.1)。
|
||||||
|
*
|
||||||
|
* 配方是**数据不是代码**:装进来仍然只是数据,可静态审、可回放、可在治理台上摆出
|
||||||
|
* 「这一步系统会做什么」。执行第三方代码则相反 —— ADR-004 的立场是「不执行包代码即知包为何物」。
|
||||||
|
*
|
||||||
|
* **只声明真实现了的那几式。** 表达力有上限是刻意的:上限之外的不要硬塞,
|
||||||
|
* 退回模型,或者把那一段判断从这一步里切出去。
|
||||||
|
*/
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
/** 表达式 —— 递归,`z.lazy` 是唯一写法(zod 的自引用) */
|
||||||
|
export type AutoExpr =
|
||||||
|
| string
|
||||||
|
| number
|
||||||
|
| boolean
|
||||||
|
/** 入参 / 基座读数 / `let` 里算好的值 —— 三者同一个命名空间,`let` 优先 */
|
||||||
|
| { readonly in: string }
|
||||||
|
/** 本行的列 */
|
||||||
|
| { readonly col: string }
|
||||||
|
/** 外层那一行的列(两层嵌套时) */
|
||||||
|
| { readonly outer: string }
|
||||||
|
/** 拼接,一律按字符串 */
|
||||||
|
| { readonly cat: readonly AutoExpr[] }
|
||||||
|
/** 只留数字(铸号:`case:openedAt` → `20260829215103`) */
|
||||||
|
| { readonly fmt: AutoExpr; readonly as: 'digits' }
|
||||||
|
/** 查另一张表的一列;查不到给 `else`(缺省空串) */
|
||||||
|
| {
|
||||||
|
readonly lookup: string;
|
||||||
|
readonly by: Readonly<Record<string, AutoExpr>>;
|
||||||
|
readonly take: string;
|
||||||
|
readonly else?: AutoExpr | undefined;
|
||||||
|
}
|
||||||
|
/** S-FEEL 条件(可嵌套)—— 已算出的同行字段进它的作用域 */
|
||||||
|
| { readonly when: string; readonly then: AutoExpr; readonly else: AutoExpr };
|
||||||
|
|
||||||
|
export const autoExprSchema: z.ZodType<AutoExpr> = z.lazy(() =>
|
||||||
|
z.union([
|
||||||
|
z.string(),
|
||||||
|
z.number(),
|
||||||
|
z.boolean(),
|
||||||
|
z.object({ in: z.string().min(1) }).strict(),
|
||||||
|
z.object({ col: z.string().min(1) }).strict(),
|
||||||
|
z.object({ outer: z.string().min(1) }).strict(),
|
||||||
|
z.object({ cat: z.array(autoExprSchema).min(1) }).strict(),
|
||||||
|
z.object({ fmt: autoExprSchema, as: z.literal('digits') }).strict(),
|
||||||
|
z
|
||||||
|
.object({
|
||||||
|
lookup: z.string().min(1),
|
||||||
|
by: z.record(z.string(), autoExprSchema),
|
||||||
|
take: z.string().min(1),
|
||||||
|
else: autoExprSchema.optional(),
|
||||||
|
})
|
||||||
|
.strict(),
|
||||||
|
z.object({ when: z.string().min(1), then: autoExprSchema, else: autoExprSchema }).strict(),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据源 —— 这一笔要对着几行办。
|
||||||
|
*
|
||||||
|
* 地址只有四种(spec §5.1):`域.对象`(**任务视图 = 正式表 + 本单草稿叠加**,默认该用的那一种)·
|
||||||
|
* `mine:域.对象`(只有本单草稿写下的行)· `in:入参数组` · `result:xxx`(本拍 analyze 的出参)。
|
||||||
|
* **没有「纯正式表」那一种** —— 要区分「本单写的」与「别人放行的」,`mine:` 就够了。
|
||||||
|
*/
|
||||||
|
export type AutoSource =
|
||||||
|
| { readonly once: true }
|
||||||
|
| { readonly rows: readonly Readonly<Record<string, AutoExpr>>[] }
|
||||||
|
| {
|
||||||
|
readonly each: string;
|
||||||
|
readonly where?: Readonly<Record<string, AutoExpr>> | undefined;
|
||||||
|
/** 第二层,**到此为止** —— 三层以上就别 auto 了 */
|
||||||
|
readonly then?:
|
||||||
|
| { readonly each: string; readonly where?: Readonly<Record<string, AutoExpr>> | undefined }
|
||||||
|
| undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const autoSourceSchema: z.ZodType<AutoSource> = z.union([
|
||||||
|
z.object({ once: z.literal(true) }).strict(),
|
||||||
|
z.object({ rows: z.array(z.record(z.string(), autoExprSchema)).min(1) }).strict(),
|
||||||
|
z
|
||||||
|
.object({
|
||||||
|
each: z.string().min(1),
|
||||||
|
where: z.record(z.string(), autoExprSchema).optional(),
|
||||||
|
then: z
|
||||||
|
.object({ each: z.string().min(1), where: z.record(z.string(), autoExprSchema).optional() })
|
||||||
|
.strict()
|
||||||
|
.optional(),
|
||||||
|
})
|
||||||
|
.strict(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
/** 五个动词 —— `when` 为假就跳过这一笔 */
|
||||||
|
export type AutoAction =
|
||||||
|
| {
|
||||||
|
readonly kind: 'project';
|
||||||
|
readonly when?: string | undefined;
|
||||||
|
/** 台账地址「域.对象」—— 必须在 `declarations.ledger` 里(静态审) */
|
||||||
|
readonly into: string;
|
||||||
|
/** 这一笔投影出来的行**就是这张表在本单草稿里的全部**,多出来的撤掉。只动草稿层 */
|
||||||
|
readonly prune?: boolean | undefined;
|
||||||
|
readonly from: AutoSource;
|
||||||
|
readonly fields: Readonly<Record<string, AutoExpr>>;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
readonly kind: 'analyze';
|
||||||
|
readonly when?: string | undefined;
|
||||||
|
/** 活动声明过的模型名 */
|
||||||
|
readonly model: string;
|
||||||
|
/** 出参落进拍内作用域 `result:<into>`,供后面的 project 用 `each`/`lookup` 消费 */
|
||||||
|
readonly into: string;
|
||||||
|
/** 给模型的 system 段 —— 说清判什么、四态各是什么意思 */
|
||||||
|
readonly instruction: string;
|
||||||
|
/** 入参由声明组装:一行一条,`fields` 决定每条摆哪几个键 */
|
||||||
|
readonly input: { readonly from: AutoSource; readonly fields: Readonly<Record<string, AutoExpr>> };
|
||||||
|
/** 出参 JSON Schema —— 顶层必须是 object */
|
||||||
|
readonly output: Readonly<Record<string, unknown>>;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
readonly kind: 'card';
|
||||||
|
readonly when?: string | undefined;
|
||||||
|
readonly as: 'execution' | 'decide';
|
||||||
|
readonly variant?: ('data' | 'review') | undefined;
|
||||||
|
readonly title: AutoExpr;
|
||||||
|
/** 决策卡问的那句话 */
|
||||||
|
readonly ask?: AutoExpr | undefined;
|
||||||
|
/** 作业指导书 —— 一条一句,静态文本 */
|
||||||
|
readonly guide?: readonly string[] | undefined;
|
||||||
|
readonly standard?: AutoExpr | undefined;
|
||||||
|
/** 调用必附上下文 —— **基座拒发 basis 为空的工单**,别指望兜底(它摆的是内部家具) */
|
||||||
|
readonly basis?: readonly { readonly k: string; readonly v: AutoExpr }[] | undefined;
|
||||||
|
/** 这张卡的提交闸盯哪几张表 */
|
||||||
|
readonly gateLedgers?: readonly string[] | undefined;
|
||||||
|
/** 决策卡的选项;`variant:'review'` 时允许只有一个 */
|
||||||
|
readonly options?:
|
||||||
|
| readonly { readonly value: string; readonly label: string; readonly detail?: string | undefined }[]
|
||||||
|
| undefined;
|
||||||
|
}
|
||||||
|
| { readonly kind: 'submit'; readonly when?: string | undefined; readonly rationale: AutoExpr }
|
||||||
|
| { readonly kind: 'say'; readonly when?: string | undefined; readonly text: AutoExpr };
|
||||||
|
|
||||||
|
export const autoActionSchema: z.ZodType<AutoAction> = z.discriminatedUnion('kind', [
|
||||||
|
z
|
||||||
|
.object({
|
||||||
|
kind: z.literal('project'),
|
||||||
|
when: z.string().min(1).optional(),
|
||||||
|
into: z.string().regex(/^[a-z_]+\.[a-z_]+$/, '台账地址写成「域.对象」'),
|
||||||
|
prune: z.boolean().optional(),
|
||||||
|
from: autoSourceSchema,
|
||||||
|
fields: z.record(z.string(), autoExprSchema),
|
||||||
|
})
|
||||||
|
.strict(),
|
||||||
|
z
|
||||||
|
.object({
|
||||||
|
kind: z.literal('analyze'),
|
||||||
|
when: z.string().min(1).optional(),
|
||||||
|
model: z.string().min(1),
|
||||||
|
into: z.string().min(1),
|
||||||
|
instruction: z.string().min(1),
|
||||||
|
input: z.object({ from: autoSourceSchema, fields: z.record(z.string(), autoExprSchema) }).strict(),
|
||||||
|
output: z.record(z.string(), z.unknown()),
|
||||||
|
})
|
||||||
|
.strict(),
|
||||||
|
z
|
||||||
|
.object({
|
||||||
|
kind: z.literal('card'),
|
||||||
|
when: z.string().min(1).optional(),
|
||||||
|
as: z.enum(['execution', 'decide']),
|
||||||
|
variant: z.enum(['data', 'review']).optional(),
|
||||||
|
title: autoExprSchema,
|
||||||
|
ask: autoExprSchema.optional(),
|
||||||
|
guide: z.array(z.string().min(1)).optional(),
|
||||||
|
standard: autoExprSchema.optional(),
|
||||||
|
basis: z.array(z.object({ k: z.string().min(1), v: autoExprSchema }).strict()).optional(),
|
||||||
|
gateLedgers: z.array(z.string().min(1)).optional(),
|
||||||
|
options: z
|
||||||
|
.array(z.object({ value: z.string().min(1), label: z.string().min(1), detail: z.string().optional() }).strict())
|
||||||
|
.optional(),
|
||||||
|
})
|
||||||
|
.strict(),
|
||||||
|
z.object({ kind: z.literal('submit'), when: z.string().min(1).optional(), rationale: autoExprSchema }).strict(),
|
||||||
|
z.object({ kind: z.literal('say'), when: z.string().min(1).optional(), text: autoExprSchema }).strict(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
export type AutoRecipe = {
|
||||||
|
/** 配方级局部值:先算一遍,后面所有表达式都能 `{in:'名'}` 引用 —— 整单只有一个案号,靠它 */
|
||||||
|
readonly let?: Readonly<Record<string, AutoExpr>> | undefined;
|
||||||
|
/** 按序执行 */
|
||||||
|
readonly do: readonly AutoAction[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const autoRecipeSchema: z.ZodType<AutoRecipe> = z
|
||||||
|
.object({
|
||||||
|
let: z.record(z.string(), autoExprSchema).optional(),
|
||||||
|
do: z.array(autoActionSchema).min(1),
|
||||||
|
})
|
||||||
|
.strict();
|
||||||
@@ -16,4 +16,5 @@ export * from './manifest.ts';
|
|||||||
export * from './org.ts';
|
export * from './org.ts';
|
||||||
export * from './mail.ts';
|
export * from './mail.ts';
|
||||||
export * from './activity-ops.ts';
|
export * from './activity-ops.ts';
|
||||||
|
export * from './auto-recipe.ts';
|
||||||
export * from './api/index.ts';
|
export * from './api/index.ts';
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { activityIdSchema, skillIdSchema } from './id.ts';
|
import { activityIdSchema, skillIdSchema } from './id.ts';
|
||||||
|
import { autoRecipeSchema } from './auto-recipe.ts';
|
||||||
import { jsonSchemaSchema, sevenPartContractSchema } from './contract7.ts';
|
import { jsonSchemaSchema, sevenPartContractSchema } from './contract7.ts';
|
||||||
import { listenerSpecSchema } from './envelope.ts';
|
import { listenerSpecSchema } from './envelope.ts';
|
||||||
import { objectDeclarationSchema } from './ledger.ts';
|
import { objectDeclarationSchema } from './ledger.ts';
|
||||||
@@ -191,6 +192,14 @@ export const planStepSchema = z.object({
|
|||||||
*/
|
*/
|
||||||
/* 可选:老活动一个字都不用改(`writes`/`reads` 用 default([]) 是历史,这里不跟 —— 加默认值会逼六个包全改) */
|
/* 可选:老活动一个字都不用改(`writes`/`reads` 用 default([]) 是历史,这里不跟 —— 加默认值会逼六个包全改) */
|
||||||
humanWrites: z.array(z.string()).optional(),
|
humanWrites: z.array(z.string()).optional(),
|
||||||
|
/**
|
||||||
|
* **这一步系统按声明自己做**(2026-08-29,ADR-014 / spec §5)。
|
||||||
|
*
|
||||||
|
* 引擎在这一步的每一拍拍首跑配方:发了卡就收拍(零 token);没发卡且这一步没有 `guide`
|
||||||
|
* 就重求 `doneWhen` 收步;有 `guide` 的照常问模型(半 auto)。
|
||||||
|
* **老活动一个字不用改** —— 不写就是不写,不是缺件。
|
||||||
|
*/
|
||||||
|
auto: autoRecipeSchema.optional(),
|
||||||
});
|
});
|
||||||
export type PlanStep = z.infer<typeof planStepSchema>;
|
export type PlanStep = z.infer<typeof planStepSchema>;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user