编排层③:活动装入/换版入口 —— 开发完能装进来,不必跑种子

此前活动只能靠 seed 装入,治理台没有任何入口 —— 这是「逐个开发活动」的拦路石。
补 POST /api/gov/activities(无代码通道:交清单 + .js 产物,都是数据),
两审在服务端跑;不过审按发布门同样的式样回 412 + ApiError.detail 带审计结论
(直接回业务体的话类型化客户端拿不到详情)。活动库给「装入 / 换版」卡:选文件或粘贴两份。
实测:0.1.0 → 0.2.0 版本并存;声明未注册的工具即 412 并点名到工具。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
This commit is contained in:
2026-07-28 10:01:25 +08:00
co-authored by Claude Fable 5
parent c8cfd61929
commit 5619d86ce4
4 changed files with 644 additions and 2 deletions
+33 -1
View File
@@ -8,7 +8,7 @@ import { disableCheckSchema, grantSchema, positionSchema, staffSchema, workGroup
import { positionIdSchema, staffIdSchema, taskIdSchema } from '../id.ts';
import { closureReportSchema, processPackageSchema, triggerConfigSchema } from '../process-package.ts';
import { controlSemanticSchema, taskRuntimeStateSchema, terminalStateSchema, workflowStateSchema } from '../terminal.ts';
import { paradigmSchema } from '../manifest.ts';
import { activityManifestSchema, paradigmSchema } from '../manifest.ts';
import { reportMetricsSchema, reportPeriodSchema } from './workbench.ts';
/* ── 员工管理 ─────────────────────────────────────────────────────────── */
@@ -278,6 +278,38 @@ export const saveWorkGroupRoute = {
output: z.object({ group: workGroupSchema }),
} as const satisfies RouteContract;
/* ── 活动库 · 装入 / 换版(安装通道) ─────────────────────────────── */
/**
* 经安装通道装入活动包 —— **无代码通道**(ADR-004):交的是清单 + .js 产物,都是数据。
*
* 安装两审在服务端跑:静态审读清单(工具契约在否、模型名绑没绑、模版有没有脚本、
* 声明与已物化的台账冲不冲),回放审重演历史(首装无历史即免,以样例集替代)。
* **恒为新增、版本并存**:装入不覆盖旧版本,在途任务按其启动时版本跑完。
*/
export const installActivityRoute = {
method: 'POST',
path: '/api/gov/activities',
body: z.object({
/** 清单六区:身份 / 七件契约 / 声明 / 资产初始版 / 样例集 / 代码入口 */
manifest: activityManifestSchema,
/** .js 产物原文 —— 只当数据存,不 import、不 eval */
artifact: z.string().min(1),
}),
output: z.object({
accepted: z.boolean(),
activityId: z.string(),
packageVersion: z.string(),
staticFindings: z.array(z.object({ code: z.string(), severity: z.string(), message: z.string() })),
replay: z.object({
oldPassStillPass: z.boolean(),
checked: z.number().int(),
skippedNoHistory: z.boolean(),
regressions: z.array(z.object({ taskId: z.string(), detail: z.string() })),
}),
}),
} as const satisfies RouteContract;
/* ── 工具管理 · 导入自定义工具 ─────────────────────────────────────── */
/**