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:
@@ -187,3 +187,20 @@ describe('活动记忆 —— 三件套与不载权力', () => {
|
||||
expect(selectForLoading(rows, { limit: 10 })).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('归侧:治理干预', () => {
|
||||
it('控制事件归人裁决侧 —— 人的叫停不得记成机器产出', () => {
|
||||
// 归侧是纯推导(按动作类型),所以「谁干的」必须体现在 kind 上,而不是靠调用方传侧。
|
||||
expect(deriveSide('control')).toBe('human-verdict');
|
||||
expect(deriveSide('engine-transition')).toBe('machine');
|
||||
});
|
||||
|
||||
it('治理干预进结构化日志骨架,模板与引擎自转可分辨', () => {
|
||||
const lines = structuredLog([
|
||||
entry('control', { seq: 1, title: '控制事件:halt' }),
|
||||
entry('engine-transition', { seq: 2, title: '步 1 完成' }),
|
||||
]);
|
||||
expect(lines.map((l) => l.title)).toEqual(['治理:控制事件:halt', '迁移:步 1 完成']);
|
||||
expect(lines.map((l) => l.sideLabel)).toEqual(['人裁决', '机器产出']);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -33,6 +33,7 @@ const SKELETON: ReadonlySet<EntryKind> = new Set<EntryKind>([
|
||||
'release-verdict',
|
||||
'terminal',
|
||||
'human-input',
|
||||
'control',
|
||||
]);
|
||||
|
||||
const TEMPLATE: Readonly<Record<EntryKind, (e: MemoryEntry) => string>> = {
|
||||
@@ -49,6 +50,7 @@ const TEMPLATE: Readonly<Record<EntryKind, (e: MemoryEntry) => string>> = {
|
||||
'release-verdict': (e) => `放行裁决:${e.title}`,
|
||||
terminal: (e) => `终态:${e.title}`,
|
||||
'human-input': (e) => `留言:${e.title}`,
|
||||
control: (e) => `治理:${e.title}`,
|
||||
};
|
||||
|
||||
/** 结构化日志:零 LLM,可重算,每条自带谁 · 何时 · 依据 · 留痕指针 */
|
||||
|
||||
@@ -21,6 +21,8 @@ export const ENTRY_KINDS = [
|
||||
'release-verdict',
|
||||
'terminal',
|
||||
'human-input',
|
||||
/** 治理干预:控制事件 / 恢复 / 改派 —— 人对过程的裁断,不是引擎自转 */
|
||||
'control',
|
||||
] as const;
|
||||
export type EntryKind = (typeof ENTRY_KINDS)[number];
|
||||
|
||||
@@ -76,6 +78,8 @@ export function deriveSide(kind: EntryKind): AttributionSide {
|
||||
case 'card-reply':
|
||||
case 'human-input':
|
||||
return 'human';
|
||||
// 治理干预由人发起:归人裁决侧,否则自主率会把人的叫停算成机器产出
|
||||
case 'control':
|
||||
case 'release-verdict':
|
||||
case 'terminal':
|
||||
return 'human-verdict';
|
||||
|
||||
@@ -172,7 +172,7 @@ export function createTaskService(deps: TaskServiceDeps): TaskService {
|
||||
await deps.events.publishControl(taskId, semantic, by, reason);
|
||||
await deps.memory.append({
|
||||
taskId,
|
||||
kind: 'engine-transition',
|
||||
kind: 'control',
|
||||
title: `控制事件:${semantic}`,
|
||||
detail: { by, reason, correctionSignal: outcome.kind === 'halted' ? 'heaviest' : undefined },
|
||||
});
|
||||
@@ -190,7 +190,7 @@ export function createTaskService(deps: TaskServiceDeps): TaskService {
|
||||
async resume(taskId, by) {
|
||||
const task = await require(taskId);
|
||||
if (task.terminal !== undefined) throw new TaskAlreadyTerminalError(taskId, task.terminal);
|
||||
await deps.memory.append({ taskId, kind: 'engine-transition', title: '恢复', detail: { by } });
|
||||
await deps.memory.append({ taskId, kind: 'control', title: '恢复', detail: { by } });
|
||||
const updated = await deps.store.update(taskId, { runtime: 'running' });
|
||||
await deps.interaction.setPhase(taskId, 'active');
|
||||
return updated;
|
||||
@@ -202,7 +202,7 @@ export function createTaskService(deps: TaskServiceDeps): TaskService {
|
||||
const updated = await deps.store.update(taskId, { ownerId: to });
|
||||
await deps.memory.append({
|
||||
taskId,
|
||||
kind: 'engine-transition',
|
||||
kind: 'control',
|
||||
title: `改派:${task.ownerId} → ${to}`,
|
||||
detail: { by },
|
||||
});
|
||||
|
||||
@@ -48,7 +48,8 @@ export type ParadigmEngine = {
|
||||
export type MemoryRecorder = {
|
||||
append(input: {
|
||||
taskId: TaskId;
|
||||
kind: 'engine-transition' | 'terminal' | 'observation';
|
||||
/** control = 治理干预(叫停 / 恢复 / 改派):人对过程的裁断,归人裁决侧 */
|
||||
kind: 'engine-transition' | 'terminal' | 'observation' | 'control';
|
||||
title: string;
|
||||
detail?: Record<string, unknown>;
|
||||
}): Promise<void>;
|
||||
|
||||
Reference in New Issue
Block a user