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:
2026-07-27 03:28:40 +08:00
co-authored by Claude Opus 5
parent 594a0a9ca0
commit a9cd6d10c7
47 changed files with 7272 additions and 11 deletions
+303 -1
View File
@@ -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>;