M6(2/n): 治理台九功能 + SSE 订阅层 + 读/治分权
治理台按 §9.1 拆成九个 gov-* feature,共用件落 shared/gov:
· 外壳(左轨/顶栏/面包屑/页签/整页下钻)自原型 Governance.tsx 平移,
DOM 与 g-* 类名原样;路由状态即应用状态,区/页签/下钻栈全编码进 hash 可深链
· 编排画布 diagram-js 画布件 1:1 平移(flow-layout/flow-node-paint/FlowDiagram),
新增 pkg-to-graph 把 ProcessPackage 转成画布图模型;统一报价 v1 全图 12 节点 15 边渲染通过
· 后端补四路由:审批单 / 工作流下钻 / 通道实例 / 度量仪表
SSE 订阅层(§9.2):app 层单一连接,按信封目标失效缓存;
组件不再自行轮询(去掉 refetchInterval)。广播只说「哪个目标动了」,不带业务载荷,
且在投递已落库已送达之后才发 —— 不是第二条投递链路。
两处实质修正:
· 治理路由一律要 govern → 读路由改要 read。客体三类 × 动作三种的分法就是为此:
BD 有 task:read 能看在途、动不了控制事件;一律 govern 等于把三种动作压成一种
· 控制事件/恢复/改派原记为 engine-transition(归机器产出)→ 新增 control 条目种类,
归人裁决侧。归侧是按动作类型的纯推导,所以「谁干的」必须体现在 kind 上;
否则人的叫停会被算进自主率的机器分子
判据 ⑩ 治理台一半跑通:在途监控 / 叫停 / 恢复,治理动作留痕可查。
无权查看不再画成空清单,按岗位说明缺哪一项。
gates: tsc / eslint / depcruise(286 模块)/ api-report / 360 测试 全绿
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
This commit is contained in:
@@ -5,7 +5,10 @@
|
||||
import { z } from 'zod';
|
||||
import type { RouteContract } from './http.ts';
|
||||
import { disableCheckSchema, grantSchema, positionSchema, staffSchema } from '../org.ts';
|
||||
import { positionIdSchema, staffIdSchema } from '../id.ts';
|
||||
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';
|
||||
|
||||
/* ── 员工管理 ─────────────────────────────────────────────────────────── */
|
||||
|
||||
@@ -109,3 +112,302 @@ export const probeLlmInstanceRoute = {
|
||||
params: z.object({ id: z.string() }),
|
||||
output: z.object({ ok: z.boolean(), latencyMs: z.number().int().optional(), error: z.string().optional() }),
|
||||
} as const satisfies RouteContract;
|
||||
|
||||
/* ── 任务管理:在途监控 · 控制事件 · 改派 ─────────────────────────────── */
|
||||
|
||||
export const govTaskRowSchema = z.object({
|
||||
id: taskIdSchema,
|
||||
title: z.string(),
|
||||
activityId: z.string(),
|
||||
paradigm: paradigmSchema,
|
||||
ownerName: z.string(),
|
||||
runtime: taskRuntimeStateSchema,
|
||||
terminal: terminalStateSchema.optional(),
|
||||
workflowId: z.string().optional(),
|
||||
usedSteps: z.number().int(),
|
||||
capSteps: z.number().int(),
|
||||
startedAt: z.string(),
|
||||
});
|
||||
export type GovTaskRow = z.infer<typeof govTaskRowSchema>;
|
||||
|
||||
export const listGovTasksRoute = {
|
||||
method: 'GET',
|
||||
path: '/api/gov/tasks',
|
||||
output: z.object({
|
||||
tasks: z.array(govTaskRowSchema),
|
||||
workflows: z.array(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
subject: z.string(),
|
||||
state: workflowStateSchema,
|
||||
packageId: z.string(),
|
||||
startedAt: z.string(),
|
||||
}),
|
||||
),
|
||||
}),
|
||||
} as const satisfies RouteContract;
|
||||
|
||||
/** 控制三语义:暂停 / 叫停 / 终止 —— 治理台的干预面(OS 06) */
|
||||
export const controlTaskRoute = {
|
||||
method: 'POST',
|
||||
path: '/api/gov/tasks/:taskId/control',
|
||||
params: z.object({ taskId: taskIdSchema }),
|
||||
body: z.object({ semantic: controlSemanticSchema, reason: z.string().optional() }),
|
||||
output: z.object({ ok: z.boolean(), resumable: z.boolean() }),
|
||||
} as const satisfies RouteContract;
|
||||
|
||||
export const resumeTaskRoute = {
|
||||
method: 'POST',
|
||||
path: '/api/gov/tasks/:taskId/resume',
|
||||
params: z.object({ taskId: taskIdSchema }),
|
||||
output: z.object({ ok: z.boolean(), runtime: taskRuntimeStateSchema }),
|
||||
} as const satisfies RouteContract;
|
||||
|
||||
/** 改派 —— 在途任务的负责人变更,留痕并通知双方 */
|
||||
export const reassignTaskRoute = {
|
||||
method: 'POST',
|
||||
path: '/api/gov/tasks/:taskId/reassign',
|
||||
params: z.object({ taskId: taskIdSchema }),
|
||||
body: z.object({ to: staffIdSchema }),
|
||||
output: z.object({ ok: z.boolean(), ownerId: staffIdSchema }),
|
||||
} as const satisfies RouteContract;
|
||||
|
||||
/** 任务日志 —— 治理台可查看任意任务的任务日志(OS 02) */
|
||||
export const govTaskLogRoute = {
|
||||
method: 'GET',
|
||||
path: '/api/gov/tasks/:taskId/log',
|
||||
params: z.object({ taskId: taskIdSchema }),
|
||||
output: z.object({
|
||||
log: z.array(z.object({ id: z.string(), at: z.string(), sideLabel: z.string(), title: z.string() })),
|
||||
}),
|
||||
} as const satisfies RouteContract;
|
||||
|
||||
/* ── 工作流管理:流程包编排与版本 ─────────────────────────────────────── */
|
||||
|
||||
export const listProcessPackagesRoute = {
|
||||
method: 'GET',
|
||||
path: '/api/gov/process',
|
||||
output: z.object({ packages: z.array(processPackageSchema) }),
|
||||
} as const satisfies RouteContract;
|
||||
|
||||
/** 画图即校验 —— 与前端画布同源的闭合校验 */
|
||||
export const validateProcessRoute = {
|
||||
method: 'POST',
|
||||
path: '/api/gov/process/validate',
|
||||
body: z.object({ pkg: processPackageSchema }),
|
||||
output: closureReportSchema,
|
||||
} as const satisfies RouteContract;
|
||||
|
||||
export const publishProcessRoute = {
|
||||
method: 'POST',
|
||||
path: '/api/gov/process/:id/:version/publish',
|
||||
params: z.object({ id: z.string(), version: z.string() }),
|
||||
output: closureReportSchema,
|
||||
} as const satisfies RouteContract;
|
||||
|
||||
/* ── 触发器配置 ──────────────────────────────────────────────────────── */
|
||||
|
||||
export const listTriggersRoute = {
|
||||
method: 'GET',
|
||||
path: '/api/gov/triggers',
|
||||
output: z.object({ triggers: z.array(triggerConfigSchema) }),
|
||||
} as const satisfies RouteContract;
|
||||
|
||||
export const toggleTriggerRoute = {
|
||||
method: 'POST',
|
||||
path: '/api/gov/triggers/:id/toggle',
|
||||
params: z.object({ id: z.string() }),
|
||||
body: z.object({ enabled: z.boolean() }),
|
||||
output: z.object({ ok: z.boolean() }),
|
||||
} as const satisfies RouteContract;
|
||||
|
||||
/* ── 活动库管理:两维分类 × 包版本 × 资产版本 ─────────────────────────── */
|
||||
|
||||
export const listActivitiesRoute = {
|
||||
method: 'GET',
|
||||
path: '/api/gov/activities',
|
||||
output: z.object({
|
||||
activities: z.array(
|
||||
z.object({
|
||||
activityId: z.string(),
|
||||
name: z.string(),
|
||||
category: z.string(),
|
||||
paradigm: paradigmSchema,
|
||||
enabled: z.boolean(),
|
||||
packageVersions: z.array(z.object({ version: z.string(), state: z.string(), installedAt: z.string() })),
|
||||
assetVersions: z.array(z.object({ kind: z.string(), version: z.number().int(), effective: z.boolean() })),
|
||||
}),
|
||||
),
|
||||
}),
|
||||
} as const satisfies RouteContract;
|
||||
|
||||
/* ── 工具管理 ────────────────────────────────────────────────────────── */
|
||||
|
||||
export const listToolsRoute = {
|
||||
method: 'GET',
|
||||
path: '/api/gov/tools',
|
||||
output: z.object({
|
||||
tools: z.array(
|
||||
z.object({
|
||||
name: z.string(),
|
||||
version: z.string(),
|
||||
description: z.string(),
|
||||
effect: z.enum(['read', 'write']),
|
||||
pure: z.boolean(),
|
||||
connectionKeys: z.array(z.string()),
|
||||
}),
|
||||
),
|
||||
}),
|
||||
} as const satisfies RouteContract;
|
||||
|
||||
/* ── 数据管理:域 / 对象目录 + 记录浏览 ───────────────────────────────── */
|
||||
|
||||
export const listLedgerObjectsRoute = {
|
||||
method: 'GET',
|
||||
path: '/api/gov/data/objects',
|
||||
output: z.object({ objects: z.array(z.object({ domain: z.string(), object: z.string() })) }),
|
||||
} as const satisfies RouteContract;
|
||||
|
||||
export const browseLedgerRoute = {
|
||||
method: 'GET',
|
||||
path: '/api/gov/data/:domain/:object',
|
||||
params: z.object({ domain: z.string(), object: z.string() }),
|
||||
output: z.object({
|
||||
columns: z.array(z.object({ name: z.string(), type: z.string() })),
|
||||
primaryKey: z.array(z.string()),
|
||||
rows: z.array(z.record(z.string(), z.unknown())),
|
||||
total: z.number().int(),
|
||||
}),
|
||||
} as const satisfies RouteContract;
|
||||
|
||||
/* ── 记忆管理:活动记忆四层 ──────────────────────────────────────────── */
|
||||
|
||||
export const listActivityMemoryRoute = {
|
||||
method: 'GET',
|
||||
path: '/api/gov/memory/:activityId',
|
||||
params: z.object({ activityId: z.string() }),
|
||||
output: z.object({
|
||||
entries: z.array(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
layer: z.string(),
|
||||
statement: z.string(),
|
||||
evidenceCount: z.number().int(),
|
||||
incrementState: z.string().optional(),
|
||||
createdAt: z.string(),
|
||||
}),
|
||||
),
|
||||
}),
|
||||
} as const satisfies RouteContract;
|
||||
|
||||
/* ── 系统管理 · 通道实例 ──────────────────────────────────────────────── */
|
||||
|
||||
/** 通道配置里本就没有凭证字段(凭证只在治理台的连接配置中,ADR-009);此处整体可回 */
|
||||
export const listChannelsRoute = {
|
||||
method: 'GET',
|
||||
path: '/api/gov/channels',
|
||||
output: z.object({
|
||||
channels: z.array(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
kind: z.enum(['mail', 'im']),
|
||||
address: z.string(),
|
||||
enabled: z.boolean(),
|
||||
extractCount: z.number().int(),
|
||||
mappingCount: z.number().int(),
|
||||
fallbackPosition: z.string().optional(),
|
||||
addressWindowMs: z.number().int(),
|
||||
}),
|
||||
),
|
||||
}),
|
||||
} as const satisfies RouteContract;
|
||||
|
||||
/* ── 任务管理 · 审批单 ────────────────────────────────────────────────── */
|
||||
|
||||
/** 审批单不是任务:没有预算、没有任务日志,只有批不批(卡在审批人工位的验收工单) */
|
||||
export const listApprovalsRoute = {
|
||||
method: 'GET',
|
||||
path: '/api/gov/approvals',
|
||||
output: z.object({
|
||||
approvals: z.array(
|
||||
z.object({
|
||||
id: z.string(),
|
||||
workflowId: z.string(),
|
||||
workflowSubject: z.string(),
|
||||
title: z.string(),
|
||||
approverName: z.string(),
|
||||
approverPosition: z.string(),
|
||||
state: z.enum(['pending', 'passed', 'rejected']),
|
||||
issuedAt: z.string(),
|
||||
}),
|
||||
),
|
||||
}),
|
||||
} as const satisfies RouteContract;
|
||||
|
||||
/* ── 工作流下钻:实例 × 节点 ──────────────────────────────────────────── */
|
||||
|
||||
export const govWorkflowRoute = {
|
||||
method: 'GET',
|
||||
path: '/api/gov/workflows/:id',
|
||||
params: z.object({ id: z.string() }),
|
||||
output: z.object({
|
||||
workflow: z.object({
|
||||
id: z.string(),
|
||||
subject: z.string(),
|
||||
state: workflowStateSchema,
|
||||
packageId: z.string(),
|
||||
packageVersion: z.number().int(),
|
||||
source: z.object({ kind: z.string(), ref: z.string(), by: z.string().optional() }),
|
||||
startedAt: z.string(),
|
||||
finishedAt: z.string().optional(),
|
||||
}),
|
||||
pkg: processPackageSchema,
|
||||
instances: z.array(
|
||||
z.object({
|
||||
nodeId: z.string(),
|
||||
instance: z.number().int(),
|
||||
running: z.boolean(),
|
||||
waiting: z.boolean(),
|
||||
activated: z.boolean(),
|
||||
terminal: terminalStateSchema.optional(),
|
||||
}),
|
||||
),
|
||||
tasks: z.array(z.object({ id: taskIdSchema, nodeId: z.string(), title: z.string(), ownerName: z.string() })),
|
||||
}),
|
||||
} as const satisfies RouteContract;
|
||||
|
||||
/* ── 记忆管理 · 度量仪表 ──────────────────────────────────────────────── */
|
||||
|
||||
/** 自主率与纠正率**成对读**,复合为有效自主率 —— 单看自主率会奖励「猛冲后返工」 */
|
||||
export const govMetricsRoute = {
|
||||
method: 'GET',
|
||||
path: '/api/gov/metrics',
|
||||
output: z.object({
|
||||
autonomy: z.number(),
|
||||
correction: z.number(),
|
||||
effectiveAutonomy: z.number(),
|
||||
interruption: z.number(),
|
||||
dmnCoverage: z.number(),
|
||||
taskCount: z.number().int(),
|
||||
}),
|
||||
} as const satisfies RouteContract;
|
||||
|
||||
/* ── 出参类型:契约自己命名,消费方不再 `z.infer` ──────────────────────── */
|
||||
|
||||
export type GovTasksOutput = z.infer<typeof listGovTasksRoute.output>;
|
||||
export type GovApprovalsOutput = z.infer<typeof listApprovalsRoute.output>;
|
||||
export type GovStaffOutput = z.infer<typeof listStaffRoute.output>;
|
||||
export type GovGrantsOutput = z.infer<typeof listGrantsRoute.output>;
|
||||
export type GovPackagesOutput = z.infer<typeof listProcessPackagesRoute.output>;
|
||||
export type GovTriggersOutput = z.infer<typeof listTriggersRoute.output>;
|
||||
export type GovActivitiesOutput = z.infer<typeof listActivitiesRoute.output>;
|
||||
export type GovToolsOutput = z.infer<typeof listToolsRoute.output>;
|
||||
export type GovObjectsOutput = z.infer<typeof listLedgerObjectsRoute.output>;
|
||||
export type GovLedgerPageOutput = z.infer<typeof browseLedgerRoute.output>;
|
||||
export type GovMemoryOutput = z.infer<typeof listActivityMemoryRoute.output>;
|
||||
export type GovMetricsOutput = z.infer<typeof govMetricsRoute.output>;
|
||||
export type GovChannelsOutput = z.infer<typeof listChannelsRoute.output>;
|
||||
export type GovLlmOutput = z.infer<typeof listLlmInstancesRoute.output>;
|
||||
export type GovWorkflowOutput = z.infer<typeof govWorkflowRoute.output>;
|
||||
export type GovTaskLogOutput = z.infer<typeof govTaskLogRoute.output>;
|
||||
|
||||
Reference in New Issue
Block a user