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
+41
View File
@@ -0,0 +1,41 @@
/**
* module: report —— 对外类型(编码规范 §10.1;语义出处 OS 2.1 / 8.1 / 8.3)。
*
* 汇报是**系统代劳的自动产出**:由本人各任务的任务日志聚合生成,确认即提交,零手工汇报。
* 度量八指标是任务日志与终态之上的**只读投影**,不设独立模块(§4 注)。
*/
import type { StaffId } from '@contracts';
import type { Metrics, ReportDraft, ReportSection } from './internal/domain/aggregate.ts';
import type { ReportPeriod, Window } from './internal/domain/period.ts';
export type { Metrics, ReportDraft, ReportPeriod, ReportSection, Window };
export declare class ReportNotFoundError extends Error {
readonly reportId: string;
constructor(reportId: string);
}
export declare class ReportAlreadySubmittedError extends Error {
readonly reportId: string;
constructor(reportId: string);
}
/** 汇报单:草案由系统生成,员工确认即提交 */
export type Report = {
readonly id: string;
readonly staffId: StaffId;
readonly period: ReportPeriod;
readonly title: string;
readonly window: Window;
readonly draft: ReportDraft;
/** 员工可在确认前补一段话 —— 唯一的手工位 */
readonly note?: string | undefined;
readonly submittedAt?: string | undefined;
readonly generatedAt: string;
};
export type ReportService = {
/** 生成(或取回)某人某周期的汇报草案 —— 未提交的重取即重算,提交后固定 */
draft(staffId: StaffId, period: ReportPeriod, anchor?: Date): Promise<Report>;
list(staffId: StaffId, limit?: number): Promise<Report[]>;
get(reportId: string): Promise<Report>;
/** 确认即提交 */
submit(reportId: string, note?: string): Promise<Report>;
/** 度量八指标 —— 治理台记忆区的仪表读数,同源同口径 */
metrics(staffId: StaffId | undefined, period: ReportPeriod, anchor?: Date): Promise<Metrics>;
};
+10
View File
@@ -0,0 +1,10 @@
/** module: report —— 唯一出口(编码规范 §10.1):纯再导出,不含实现。 */
export type { Metrics, Report, ReportDraft, ReportPeriod, ReportSection, ReportService, Window } from './contract.ts';
export { ReportAlreadySubmittedError, ReportNotFoundError } from './contract.ts';
export type { FactSource, ReportStore, ReportDeps } from './port.ts';
export type { LogFact, TaskFact } from './internal/domain/aggregate.ts';
export { computeMetrics, draftReport } from './internal/domain/aggregate.ts';
export { REPORT_PERIODS, periodLabel, windowOf } from './internal/domain/period.ts';
export { createReportService } from './internal/usecase/report-service.ts';
export { createReportStore } from './internal/adapter/report-store.ts';
export { reports } from './internal/adapter/report-schema.ts';
+29
View File
@@ -0,0 +1,29 @@
/**
* module: report 的外部所需(编码规范 §10.1)。
* 汇报**不自己记账**:事实全部来自 memory(任务日志)与 task(终态 / 预算 / 纠正信号)。
*/
import type { StaffId } from '@contracts';
import type { Report } from './contract.ts';
import type { LogFact, TaskFact } from './internal/domain/aggregate.ts';
export type ReportStore = {
list(staffId: StaffId, limit: number): Promise<Report[]>;
get(reportId: string): Promise<Report | undefined>;
findOpen(staffId: StaffId, period: string, windowFrom: string): Promise<Report | undefined>;
save(report: Report): Promise<void>;
};
/** 事实源 —— app 注入 `memory`(L2)与 `task`(L6);report 在 L8,只读不写 */
export type FactSource = {
/** 某人(或全员)在窗口内的归侧日志行 */
logsOf(staffId: StaffId | undefined, from: string, to: string): Promise<LogFact[]>;
tasksOf(staffId: StaffId | undefined): Promise<TaskFact[]>;
};
export type ReportDeps = {
readonly store: ReportStore;
readonly facts: FactSource;
readonly ids: {
next(): string;
};
readonly clock: {
now(): Date;
};
};