M6(3/n): minute 与 report —— L8 两个模块补齐

minute(纪要):工作台五要素里唯一的主动动作。
  纪要本身不产生责任,唯一通往责任的路是决议经登记触发起单 —— 登记人即来源。
  正文可续、决议只增;同一决议不重复起单(起单一刻已有一条带来源的流在跑)。

report(汇报):日/周/月/年对任务日志的聚合投影,零手工汇报。
  未提交的草案每次取回都重算 —— 汇报是投影不是账本,提交那刻才固定为事实快照。
  度量八指标落在这里(§4 注:度量是只读投影,不设独立模块):
  自主率 / 纠正率 / 有效自主率 / 打扰率 / 断点率 / DMN 覆盖率 / 单位成本 / 终态分布。
  口径全部挂归侧标签与事件流,零埋点;单位成本的分母只认放行通过。
  周窗口以周一为首日,按 UTC 切分(单组织单时区)。

FactSource 只读 memory 与 task,一行都不另记 —— 五要素同源。

gates: tsc / eslint / depcruise(305 模块)/ api-report / 377 测试 全绿
日报已在真实系统上由 A1 任务日志聚合出来,含治理干预两行

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:39:10 +08:00
co-authored by Claude Opus 5
parent a9cd6d10c7
commit a60acb2e02
37 changed files with 5495 additions and 2 deletions
+134 -1
View File
@@ -7,7 +7,7 @@ import type { RouteContract } from './http.ts';
import { sessionSchema } from '../org.ts';
import { chatMessageSchema, deliverableSchema, stageSchema, viewBlockSchema, workOrderSchema } from '../card.ts';
import { attributionSideSchema, taskRuntimeStateSchema, terminalStateSchema, workflowStateSchema } from '../terminal.ts';
import { taskIdSchema } from '../id.ts';
import { staffIdSchema, taskIdSchema } from '../id.ts';
export const sessionRoute = {
method: 'GET',
@@ -118,3 +118,136 @@ export const listMessagesRoute = {
),
}),
} as const satisfies RouteContract;
/* ── 纪要:工作台五要素里唯一的主动动作 ─────────────────────────────── */
export const minuteResolutionSchema = z.object({
id: z.string(),
text: z.string(),
ownerId: staffIdSchema.optional(),
state: z.enum(['open', 'registered']),
workflowId: z.string().optional(),
registeredById: staffIdSchema.optional(),
registeredAt: z.string().optional(),
});
export const minuteSchema = z.object({
id: z.string(),
title: z.string(),
body: z.string(),
authorId: staffIdSchema,
authorName: z.string(),
participants: z.array(z.string()),
occurredOn: z.string(),
resolutions: z.array(minuteResolutionSchema),
createdAt: z.string(),
updatedAt: z.string(),
});
export type MinuteView = z.infer<typeof minuteSchema>;
export const listMinutesRoute = {
method: 'GET',
path: '/api/minutes',
output: z.object({ minutes: z.array(minuteSchema) }),
} as const satisfies RouteContract;
export const createMinuteRoute = {
method: 'POST',
path: '/api/minutes',
body: z.object({
title: z.string().min(1),
body: z.string(),
occurredOn: z.string().min(1),
participants: z.array(staffIdSchema).default([]),
resolutions: z.array(z.object({ text: z.string().min(1), ownerId: staffIdSchema.optional() })).default([]),
}),
output: minuteSchema,
} as const satisfies RouteContract;
export const appendResolutionRoute = {
method: 'POST',
path: '/api/minutes/:minuteId/resolutions',
params: z.object({ minuteId: z.string() }),
body: z.object({ text: z.string().min(1), ownerId: staffIdSchema.optional() }),
output: minuteSchema,
} as const satisfies RouteContract;
/** 决议 → 登记起单:登记人即来源 */
export const registerResolutionRoute = {
method: 'POST',
path: '/api/minutes/resolutions/:resolutionId/register',
params: z.object({ resolutionId: z.string() }),
body: z.object({ processPackageId: z.string().min(1), data: z.record(z.string(), z.unknown()) }),
output: z.object({ workflowId: z.string(), resolutionId: z.string() }),
} as const satisfies RouteContract;
/* ── 汇报:系统代劳的自动产出 ───────────────────────────────────────── */
export const reportPeriodSchema = z.enum(['day', 'week', 'month', 'year']);
export type ReportPeriodName = z.infer<typeof reportPeriodSchema>;
export const REPORT_PERIOD_LABEL: Readonly<Record<ReportPeriodName, string>> = {
day: '日报',
week: '周报',
month: '月报',
year: '年报',
};
export const reportMetricsSchema = z.object({
autonomy: z.number(),
correction: z.number(),
effectiveAutonomy: z.number(),
interruption: z.number(),
breakpoint: z.number(),
dmnCoverage: z.number(),
unitCost: z.object({ tokens: z.number(), minutes: z.number(), humanCalls: z.number() }),
terminalMix: z.record(z.string(), z.number()),
});
export const reportSchema = z.object({
id: z.string(),
period: reportPeriodSchema,
title: z.string(),
window: z.object({ from: z.string(), to: z.string() }),
sections: z.array(
z.object({
taskId: z.string(),
title: z.string(),
activityId: z.string(),
terminalLabel: z.string().optional(),
lines: z.array(z.string()),
}),
),
metrics: reportMetricsSchema,
counts: z.object({ touched: z.number().int(), released: z.number().int(), logLines: z.number().int() }),
note: z.string().optional(),
submittedAt: z.string().optional(),
generatedAt: z.string(),
});
export type ReportView = z.infer<typeof reportSchema>;
/** 未提交的草案每次取回都重算 —— 汇报是投影,不是账本 */
export const draftReportRoute = {
method: 'GET',
path: '/api/reports/:period',
params: z.object({ period: reportPeriodSchema }),
output: reportSchema,
} as const satisfies RouteContract;
export const listReportsRoute = {
method: 'GET',
path: '/api/reports',
output: z.object({ reports: z.array(reportSchema) }),
} as const satisfies RouteContract;
/** 确认即提交,提交后不再改 */
export const submitReportRoute = {
method: 'POST',
path: '/api/reports/:reportId/submit',
params: z.object({ reportId: z.string() }),
body: z.object({ note: z.string().optional() }),
output: reportSchema,
} as const satisfies RouteContract;
export type ListMinutesOutput = z.infer<typeof listMinutesRoute.output>;
export type ListReportsOutput = z.infer<typeof listReportsRoute.output>;