@dommaker/harness 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +0 -2
  3. package/dist/cli/commands/definitions.d.ts.map +1 -1
  4. package/dist/cli/commands/definitions.js +1 -32
  5. package/dist/cli/commands/definitions.js.map +1 -1
  6. package/dist/cli/commands/passes-gate.d.ts +0 -2
  7. package/dist/cli/commands/passes-gate.d.ts.map +1 -1
  8. package/dist/cli/commands/passes-gate.js +0 -1
  9. package/dist/cli/commands/passes-gate.js.map +1 -1
  10. package/dist/cli/commands/performance.d.ts +0 -4
  11. package/dist/cli/commands/performance.d.ts.map +1 -1
  12. package/dist/cli/commands/performance.js +10 -20
  13. package/dist/cli/commands/performance.js.map +1 -1
  14. package/dist/cli/commands/report.d.ts +1 -1
  15. package/dist/cli/commands/report.d.ts.map +1 -1
  16. package/dist/cli/commands/report.js +2 -55
  17. package/dist/cli/commands/report.js.map +1 -1
  18. package/dist/cli/commands/status.d.ts.map +1 -1
  19. package/dist/cli/commands/status.js +4 -8
  20. package/dist/cli/commands/status.js.map +1 -1
  21. package/dist/context/index.d.ts +2 -5
  22. package/dist/context/index.d.ts.map +1 -1
  23. package/dist/context/index.js +2 -8
  24. package/dist/context/index.js.map +1 -1
  25. package/dist/context/token-budget.d.ts +0 -29
  26. package/dist/context/token-budget.d.ts.map +1 -1
  27. package/dist/context/token-budget.js +1 -65
  28. package/dist/context/token-budget.js.map +1 -1
  29. package/dist/context/types.d.ts +0 -1
  30. package/dist/context/types.d.ts.map +1 -1
  31. package/dist/context/types.js +0 -9
  32. package/dist/context/types.js.map +1 -1
  33. package/dist/core/constraints/checker.d.ts +14 -0
  34. package/dist/core/constraints/checker.d.ts.map +1 -1
  35. package/dist/core/constraints/checker.js +22 -2
  36. package/dist/core/constraints/checker.js.map +1 -1
  37. package/dist/core/constraints/git-evidence.d.ts +6 -3
  38. package/dist/core/constraints/git-evidence.d.ts.map +1 -1
  39. package/dist/core/constraints/git-evidence.js +6 -3
  40. package/dist/core/constraints/git-evidence.js.map +1 -1
  41. package/dist/core/index.d.ts +1 -2
  42. package/dist/core/index.d.ts.map +1 -1
  43. package/dist/core/index.js +1 -9
  44. package/dist/core/index.js.map +1 -1
  45. package/dist/core/spec/validator.d.ts +2 -0
  46. package/dist/core/spec/validator.d.ts.map +1 -1
  47. package/dist/core/spec/validator.js +9 -11
  48. package/dist/core/spec/validator.js.map +1 -1
  49. package/dist/core/validators/index.d.ts +1 -1
  50. package/dist/core/validators/index.d.ts.map +1 -1
  51. package/dist/core/validators/passes-gate.d.ts +1 -15
  52. package/dist/core/validators/passes-gate.d.ts.map +1 -1
  53. package/dist/core/validators/passes-gate.js +0 -125
  54. package/dist/core/validators/passes-gate.js.map +1 -1
  55. package/dist/failure/index.d.ts +0 -1
  56. package/dist/failure/index.d.ts.map +1 -1
  57. package/dist/failure/index.js +1 -7
  58. package/dist/failure/index.js.map +1 -1
  59. package/dist/gates/definitions.d.ts.map +1 -1
  60. package/dist/gates/definitions.js +0 -4
  61. package/dist/gates/definitions.js.map +1 -1
  62. package/dist/gates/performance.d.ts +7 -22
  63. package/dist/gates/performance.d.ts.map +1 -1
  64. package/dist/gates/performance.js +7 -89
  65. package/dist/gates/performance.js.map +1 -1
  66. package/dist/gates/types.d.ts +2 -8
  67. package/dist/gates/types.d.ts.map +1 -1
  68. package/dist/index.d.ts +6 -8
  69. package/dist/index.d.ts.map +1 -1
  70. package/dist/index.js +4 -22
  71. package/dist/index.js.map +1 -1
  72. package/dist/knowledge/index.d.ts +0 -1
  73. package/dist/knowledge/index.d.ts.map +1 -1
  74. package/dist/knowledge/index.js +1 -3
  75. package/dist/knowledge/index.js.map +1 -1
  76. package/dist/monitoring/trace-analyzer.d.ts +25 -10
  77. package/dist/monitoring/trace-analyzer.d.ts.map +1 -1
  78. package/dist/monitoring/trace-analyzer.js +147 -116
  79. package/dist/monitoring/trace-analyzer.js.map +1 -1
  80. package/dist/types/passes-gate.d.ts +0 -19
  81. package/dist/types/passes-gate.d.ts.map +1 -1
  82. package/package.json +1 -1
  83. package/src/__tests__/passes-gate-check.test.ts +20 -4
  84. package/src/__tests__/passes-gate.test.ts +5 -99
  85. package/src/__tests__/performance-gate.test.ts +3 -11
  86. package/src/__tests__/public-exports.test.ts +46 -16
  87. package/src/__tests__/trace-analyzer.test.ts +52 -2
  88. package/src/cli/commands/CONTEXT.md +3 -2
  89. package/src/cli/commands/__tests__/passes-gate-dead-options.test.ts +70 -0
  90. package/src/cli/commands/__tests__/passes-gate.test.ts +7 -3
  91. package/src/cli/commands/__tests__/performance.test.ts +5 -16
  92. package/src/cli/commands/__tests__/registry.test.ts +46 -3
  93. package/src/cli/commands/__tests__/report-extra.test.ts +0 -19
  94. package/src/cli/commands/__tests__/report-false-green.test.ts +46 -0
  95. package/src/cli/commands/__tests__/report.test.ts +0 -20
  96. package/src/cli/commands/__tests__/status-analysis-wiring.test.ts +68 -0
  97. package/src/cli/commands/__tests__/status-extra.test.ts +63 -116
  98. package/src/cli/commands/__tests__/status.test.ts +66 -148
  99. package/src/cli/commands/definitions.ts +1 -32
  100. package/src/cli/commands/passes-gate.ts +0 -3
  101. package/src/cli/commands/performance.ts +13 -27
  102. package/src/cli/commands/report.ts +3 -61
  103. package/src/cli/commands/status.ts +5 -10
  104. package/src/context/CONTEXT.md +2 -3
  105. package/src/context/__tests__/token-budget.test.ts +1 -81
  106. package/src/context/index.ts +2 -7
  107. package/src/context/token-budget.ts +0 -73
  108. package/src/context/types.ts +0 -9
  109. package/src/core/CONTEXT.md +5 -6
  110. package/src/core/constraints/__tests__/collect-constraints.test.ts +66 -0
  111. package/src/core/constraints/checker.ts +33 -2
  112. package/src/core/constraints/git-evidence.ts +6 -3
  113. package/src/core/index.ts +0 -11
  114. package/src/core/spec/__tests__/validator.test.ts +24 -0
  115. package/src/core/spec/validator.ts +9 -10
  116. package/src/core/validators/__tests__/passes-gate.test.ts +2 -9
  117. package/src/core/validators/index.ts +0 -1
  118. package/src/core/validators/passes-gate.ts +1 -149
  119. package/src/failure/CONTEXT.md +8 -6
  120. package/src/failure/index.ts +0 -10
  121. package/src/gates/CONTEXT.md +3 -2
  122. package/src/gates/__tests__/performance.test.ts +6 -74
  123. package/src/gates/definitions.ts +0 -4
  124. package/src/gates/performance.ts +9 -111
  125. package/src/gates/types.ts +2 -8
  126. package/src/index.ts +1 -36
  127. package/src/knowledge/CONTEXT.md +1 -2
  128. package/src/knowledge/index.ts +0 -1
  129. package/src/monitoring/CONTEXT.md +2 -2
  130. package/src/monitoring/trace-analyzer.ts +160 -126
  131. package/src/types/passes-gate.ts +0 -26
  132. package/src/utils/__tests__/jsonl-skip-disposition.test.ts +0 -1
  133. package/dist/cli/commands/analyze-sessions.d.ts +0 -20
  134. package/dist/cli/commands/analyze-sessions.d.ts.map +0 -1
  135. package/dist/cli/commands/analyze-sessions.js +0 -290
  136. package/dist/cli/commands/analyze-sessions.js.map +0 -1
  137. package/dist/cli/commands/update-user-model.d.ts +0 -30
  138. package/dist/cli/commands/update-user-model.d.ts.map +0 -1
  139. package/dist/cli/commands/update-user-model.js +0 -406
  140. package/dist/cli/commands/update-user-model.js.map +0 -1
  141. package/dist/cli/session-mining/corrections.d.ts +0 -17
  142. package/dist/cli/session-mining/corrections.d.ts.map +0 -1
  143. package/dist/cli/session-mining/corrections.js +0 -58
  144. package/dist/cli/session-mining/corrections.js.map +0 -1
  145. package/dist/cli/session-mining/index.d.ts +0 -13
  146. package/dist/cli/session-mining/index.d.ts.map +0 -1
  147. package/dist/cli/session-mining/index.js +0 -29
  148. package/dist/cli/session-mining/index.js.map +0 -1
  149. package/dist/cli/session-mining/text.d.ts +0 -31
  150. package/dist/cli/session-mining/text.d.ts.map +0 -1
  151. package/dist/cli/session-mining/text.js +0 -126
  152. package/dist/cli/session-mining/text.js.map +0 -1
  153. package/dist/cli/session-mining/transcript.d.ts +0 -34
  154. package/dist/cli/session-mining/transcript.d.ts.map +0 -1
  155. package/dist/cli/session-mining/transcript.js +0 -130
  156. package/dist/cli/session-mining/transcript.js.map +0 -1
  157. package/dist/context/compaction.d.ts +0 -57
  158. package/dist/context/compaction.d.ts.map +0 -1
  159. package/dist/context/compaction.js +0 -165
  160. package/dist/context/compaction.js.map +0 -1
  161. package/dist/core/session/clean-state.d.ts +0 -17
  162. package/dist/core/session/clean-state.d.ts.map +0 -1
  163. package/dist/core/session/clean-state.js +0 -192
  164. package/dist/core/session/clean-state.js.map +0 -1
  165. package/dist/core/session/index.d.ts +0 -6
  166. package/dist/core/session/index.d.ts.map +0 -1
  167. package/dist/core/session/index.js +0 -15
  168. package/dist/core/session/index.js.map +0 -1
  169. package/dist/core/session/startup.d.ts +0 -47
  170. package/dist/core/session/startup.d.ts.map +0 -1
  171. package/dist/core/session/startup.js +0 -263
  172. package/dist/core/session/startup.js.map +0 -1
  173. package/dist/failure/constraint-handler.d.ts +0 -83
  174. package/dist/failure/constraint-handler.d.ts.map +0 -1
  175. package/dist/failure/constraint-handler.js +0 -138
  176. package/dist/failure/constraint-handler.js.map +0 -1
  177. package/dist/knowledge/lifecycle-hooks.d.ts +0 -62
  178. package/dist/knowledge/lifecycle-hooks.d.ts.map +0 -1
  179. package/dist/knowledge/lifecycle-hooks.js +0 -143
  180. package/dist/knowledge/lifecycle-hooks.js.map +0 -1
  181. package/dist/types/session.d.ts +0 -113
  182. package/dist/types/session.d.ts.map +0 -1
  183. package/dist/types/session.js +0 -6
  184. package/dist/types/session.js.map +0 -1
  185. package/src/__tests__/clean-state-extra.test.ts +0 -301
  186. package/src/__tests__/clean-state.test.ts +0 -169
  187. package/src/__tests__/constraint-handler.test.ts +0 -132
  188. package/src/__tests__/passes-gate-extra.test.ts +0 -93
  189. package/src/__tests__/session-startup.test.ts +0 -334
  190. package/src/cli/commands/__tests__/analyze-sessions.test.ts +0 -184
  191. package/src/cli/commands/__tests__/update-user-model.test.ts +0 -268
  192. package/src/cli/commands/analyze-sessions.ts +0 -335
  193. package/src/cli/commands/update-user-model.ts +0 -476
  194. package/src/cli/session-mining/__tests__/transcript.test.ts +0 -118
  195. package/src/cli/session-mining/corrections.ts +0 -56
  196. package/src/cli/session-mining/index.ts +0 -13
  197. package/src/cli/session-mining/text.ts +0 -117
  198. package/src/cli/session-mining/transcript.ts +0 -125
  199. package/src/context/__tests__/compaction.test.ts +0 -169
  200. package/src/context/compaction.ts +0 -201
  201. package/src/core/session/__tests__/startup.test.ts +0 -281
  202. package/src/core/session/clean-state.ts +0 -178
  203. package/src/core/session/index.ts +0 -15
  204. package/src/core/session/startup.ts +0 -253
  205. package/src/failure/constraint-handler.ts +0 -171
  206. package/src/knowledge/__tests__/lifecycle-hooks.test.ts +0 -140
  207. package/src/knowledge/lifecycle-hooks.ts +0 -202
  208. package/src/types/session.ts +0 -132
@@ -1,253 +0,0 @@
1
- /**
2
- * SessionStartup - Session 启动检查点验证
3
- *
4
- * 确保 Agent 启动时必须先读上下文,防止"失忆"乱搞。
5
- */
6
-
7
- import { execAsync } from '../../utils/exec';
8
- import * as fs from 'fs/promises';
9
- import * as path from 'path';
10
- import * as yaml from 'js-yaml';
11
- import type {
12
- StartupCheckpoints,
13
- StartupCheckpointResult,
14
- StartupCheckpointType,
15
- TaskListJson,
16
- } from '../../types/session';
17
-
18
-
19
- const DEFAULT_TIMEOUT = 30000; // 30 seconds
20
-
21
- export class SessionStartup {
22
- private checkpoints: StartupCheckpoints;
23
- private workDir: string;
24
-
25
- constructor(workDir: string, checkpoints: StartupCheckpoints) {
26
- this.workDir = workDir;
27
- this.checkpoints = checkpoints;
28
- }
29
-
30
- async run(): Promise<{ success: boolean; results: StartupCheckpointResult[]; errors: string[] }> {
31
- const results: StartupCheckpointResult[] = [];
32
- const errors: string[] = [];
33
-
34
- // 1. Run required checkpoints
35
- for (const type of this.checkpoints.required) {
36
- const result = await this.runCheckpoint(type);
37
- results.push(result);
38
- if (!result.success) {
39
- errors.push(`Required checkpoint failed: ${type} - ${result.error}`);
40
- }
41
- }
42
-
43
- // 2. Run optional checkpoints (don't fail on error)
44
- for (const type of this.checkpoints.optional || []) {
45
- const result = await this.runCheckpoint(type);
46
- results.push(result);
47
- }
48
-
49
- return {
50
- success: errors.length === 0,
51
- results,
52
- errors,
53
- };
54
- }
55
-
56
- private async runCheckpoint(type: StartupCheckpointType): Promise<StartupCheckpointResult> {
57
- const start = Date.now();
58
-
59
- try {
60
- let data: any;
61
-
62
- switch (type) {
63
- case 'pwd':
64
- data = await this.checkPwd();
65
- break;
66
- case 'git_log':
67
- data = await this.checkGitLog();
68
- break;
69
- case 'git_status':
70
- data = await this.checkGitStatus();
71
- break;
72
- case 'read_progress':
73
- data = await this.checkProgress();
74
- break;
75
- case 'read_task_list':
76
- data = await this.checkTaskList();
77
- break;
78
- case 'init_sh':
79
- data = await this.checkInitSh();
80
- break;
81
- case 'basic_verification':
82
- data = await this.checkBasicVerification();
83
- break;
84
- case 'load_context':
85
- data = await this.checkLoadContext();
86
- break;
87
- default:
88
- throw new Error(`Unknown checkpoint type: ${type}`);
89
- }
90
-
91
- return { type, success: true, data, duration: Date.now() - start };
92
- } catch (error: any) {
93
- return { type, success: false, error: error.message, duration: Date.now() - start };
94
- }
95
- }
96
-
97
- private async checkPwd(): Promise<string> {
98
- return this.workDir;
99
- }
100
-
101
- private async checkGitLog(): Promise<string[]> {
102
- try {
103
- const { stdout } = await execAsync('git log --oneline -10', { cwd: this.workDir });
104
- return stdout.split('\n').filter(Boolean);
105
- } catch {
106
- return [];
107
- }
108
- }
109
-
110
- private async checkGitStatus(): Promise<{ staged: string[]; unstaged: string[]; untracked: string[] }> {
111
- try {
112
- const { stdout } = await execAsync('git status --porcelain', { cwd: this.workDir });
113
- const staged: string[] = [], unstaged: string[] = [], untracked: string[] = [];
114
-
115
- for (const line of stdout.split('\n').filter(Boolean)) {
116
- const status = line.substring(0, 2);
117
- const file = line.substring(3);
118
- if (status.includes('??')) untracked.push(file);
119
- else if (status.match(/[MADRC]/)) staged.push(file);
120
- else unstaged.push(file);
121
- }
122
-
123
- return { staged, unstaged, untracked };
124
- } catch {
125
- return { staged: [], unstaged: [], untracked: [] };
126
- }
127
- }
128
-
129
- private async checkProgress(): Promise<any> {
130
- const progressPath = path.join(this.workDir, '.harness', 'progress.yml');
131
- try {
132
- const content = await fs.readFile(progressPath, 'utf-8');
133
- return yaml.load(content) as any;
134
- } catch {
135
- return null;
136
- }
137
- }
138
-
139
- private async checkTaskList(): Promise<TaskListJson | null> {
140
- // Try tasks.yml first (existing format)
141
- const tasksPath = path.join(this.workDir, 'tasks.yml');
142
- try {
143
- const content = await fs.readFile(tasksPath, 'utf-8');
144
- return yaml.load(content) as TaskListJson;
145
- } catch {
146
- // Try tasks.json
147
- const jsonPath = path.join(this.workDir, 'tasks.json');
148
- try {
149
- const content = await fs.readFile(jsonPath, 'utf-8');
150
- return JSON.parse(content);
151
- } catch {
152
- return null;
153
- }
154
- }
155
- }
156
-
157
- private async checkInitSh(): Promise<boolean> {
158
- const initPath = path.join(this.workDir, 'init.sh');
159
- try {
160
- await fs.access(initPath);
161
- return true;
162
- } catch {
163
- return false;
164
- }
165
- }
166
-
167
- private async checkBasicVerification(): Promise<{ hasPackageJson: boolean; hasTests: boolean; hasReadme: boolean }> {
168
- const [hasPackageJson, hasTests, hasReadme] = await Promise.all([
169
- fs.access(path.join(this.workDir, 'package.json')).then(() => true).catch(() => false),
170
- this.hasTestFiles(),
171
- fs.access(path.join(this.workDir, 'README.md')).then(() => true).catch(() => false),
172
- ]);
173
- return { hasPackageJson, hasTests, hasReadme };
174
- }
175
-
176
- private async hasTestFiles(): Promise<boolean> {
177
- const testPatterns = ['test', 'spec', '__tests__'];
178
- try {
179
- const files = await fs.readdir(this.workDir);
180
- return files.some(f => testPatterns.some(p => f.toLowerCase().includes(p)));
181
- } catch {
182
- return false;
183
- }
184
- }
185
-
186
- private async checkLoadContext(): Promise<any> {
187
- const context: Record<string, string> = {};
188
- const files = this.checkpoints.contextFiles || [];
189
-
190
- for (const file of files) {
191
- try {
192
- context[file] = await fs.readFile(path.join(this.workDir, file), 'utf-8');
193
- } catch {}
194
- }
195
-
196
- return context;
197
- }
198
-
199
- /**
200
- * Get current task from task list
201
- */
202
- async getCurrentTask(): Promise<{ task: any; index: number } | null> {
203
- const taskList = await this.checkTaskList();
204
- if (!taskList?.tasks) return null;
205
-
206
- for (let i = 0; i < taskList.tasks.length; i++) {
207
- const task = taskList.tasks[i];
208
- if (task && !task.passes) {
209
- return { task, index: i };
210
- }
211
- }
212
-
213
- return null;
214
- }
215
-
216
- /**
217
- * Generate startup report
218
- */
219
- generateReport(results: StartupCheckpointResult[]): string {
220
- const lines: string[] = ['## Session Startup Report\n'];
221
-
222
- for (const result of results) {
223
- const icon = result.success ? '✅' : '❌';
224
- lines.push(`${icon} ${result.type} (${result.duration}ms)`);
225
- if (result.error) {
226
- lines.push(` Error: ${result.error}`);
227
- }
228
- }
229
-
230
- return lines.join('\n');
231
- }
232
- }
233
-
234
- export const createSessionStartup = (workDir: string, checkpoints: StartupCheckpoints) =>
235
- new SessionStartup(workDir, checkpoints);
236
-
237
- /**
238
- * Default startup checkpoints for code workflows
239
- */
240
- export const DEFAULT_CODE_CHECKPOINTS: StartupCheckpoints = {
241
- required: ['pwd', 'git_log', 'read_task_list'],
242
- optional: ['read_progress', 'init_sh', 'basic_verification'],
243
- timeout: DEFAULT_TIMEOUT,
244
- };
245
-
246
- /**
247
- * Minimal startup checkpoints
248
- */
249
- export const MINIMAL_CHECKPOINTS: StartupCheckpoints = {
250
- required: ['pwd', 'read_task_list'],
251
- optional: [],
252
- timeout: DEFAULT_TIMEOUT,
253
- };
@@ -1,171 +0,0 @@
1
- /**
2
- * ConstraintViolationHandler — 约束违规统一处理(S4)
3
- *
4
- * 问题:ConstraintViolationError 在 4 个调用点有 4 种行为:
5
- * 1. 抛异常阻断(checker.ts)
6
- * 2. 返回 null(report.ts catch 后返回空数据)
7
- * 3. 返回 403 HTTP 状态
8
- * 4. console.warn 静默吞掉
9
- *
10
- * 解决:提供 3 种标准化处理策略,消除 ad-hoc catch 行为。
11
- */
12
-
13
- import type { ConstraintCheckResult } from '../types/constraint';
14
-
15
- /**
16
- * 违规处理策略
17
- */
18
- export type ViolationStrategy = 'BLOCK' | 'COLLECT' | 'SAFE_BOOLEAN';
19
-
20
- /**
21
- * 违规处理结果
22
- */
23
- export interface ViolationHandlingResult {
24
- /** 策略 */
25
- strategy: ViolationStrategy;
26
- /** 检查结果(COLLECT 策略时总是有值) */
27
- checkResult?: ConstraintCheckResult;
28
- /** 是否有违规(SAFE_BOOLEAN 策略时为 false) */
29
- hasViolation: boolean;
30
- /** 违规数量 */
31
- violationCount: number;
32
- }
33
-
34
- /**
35
- * 约束违规处理器
36
- *
37
- * 用法:
38
- * ```typescript
39
- * const handler = new ConstraintViolationHandler();
40
- *
41
- * // 策略 1: BLOCK — 抛异常阻断(Iron Laws 默认)
42
- * await handler.execute(checkFn, 'BLOCK');
43
- *
44
- * // 策略 2: COLLECT — 收集违规不抛异常(用于报告生成)
45
- * const result = await handler.execute(checkFn, 'COLLECT');
46
- * // result.checkResult 总是有值
47
- *
48
- * // 策略 3: SAFE_BOOLEAN — 返回真/假(用于 canProceed 类方法)
49
- * const ok = await handler.execute(checkFn, 'SAFE_BOOLEAN');
50
- * // ok.hasViolation 为 true/false
51
- * ```
52
- */
53
- export class ConstraintViolationHandler {
54
- /**
55
- * 执行约束检查并统一处理结果
56
- *
57
- * @param checkFn 约束检查函数(可能抛出 ConstraintViolationError)
58
- * @param strategy 处理策略
59
- * @returns 标准化结果
60
- */
61
- async execute(
62
- checkFn: () => Promise<ConstraintCheckResult>,
63
- strategy: ViolationStrategy
64
- ): Promise<ViolationHandlingResult> {
65
- switch (strategy) {
66
- case 'BLOCK':
67
- return this.handleBlock(checkFn);
68
- case 'COLLECT':
69
- return this.handleCollect(checkFn);
70
- case 'SAFE_BOOLEAN':
71
- return this.handleSafeBoolean(checkFn);
72
- }
73
- }
74
-
75
- /**
76
- * BLOCK 策略:违规即抛出,无违规返回结果
77
- */
78
- private async handleBlock(
79
- checkFn: () => Promise<ConstraintCheckResult>
80
- ): Promise<ViolationHandlingResult> {
81
- const result = await checkFn();
82
- return {
83
- strategy: 'BLOCK',
84
- checkResult: result,
85
- hasViolation: !result.passed,
86
- violationCount: result.ironLaws.filter(r => !r.satisfied).length,
87
- };
88
- }
89
-
90
- /**
91
- * COLLECT 策略:捕获异常,收集所有违规,不抛出
92
- */
93
- private async handleCollect(
94
- checkFn: () => Promise<ConstraintCheckResult>
95
- ): Promise<ViolationHandlingResult> {
96
- try {
97
- const result = await checkFn();
98
- const ironViolations = result.ironLaws.filter(r => !r.satisfied).length;
99
- const guideViolations = result.warningCount;
100
- return {
101
- strategy: 'COLLECT',
102
- checkResult: result,
103
- hasViolation: !result.passed || guideViolations > 0,
104
- violationCount: ironViolations + guideViolations,
105
- };
106
- } catch {
107
- // 捕获 ConstraintViolationError,返回失败结果
108
- const empty: ConstraintCheckResult = {
109
- ironLaws: [],
110
- guidelines: [],
111
- passed: false,
112
- warningCount: 0,
113
- };
114
- return {
115
- strategy: 'COLLECT',
116
- checkResult: empty,
117
- hasViolation: true,
118
- violationCount: 1,
119
- };
120
- }
121
- }
122
-
123
- /**
124
- * SAFE_BOOLEAN 策略:不抛异常,只返回 boolean
125
- */
126
- private async handleSafeBoolean(
127
- checkFn: () => Promise<ConstraintCheckResult>
128
- ): Promise<ViolationHandlingResult> {
129
- try {
130
- const result = await checkFn();
131
- return {
132
- strategy: 'SAFE_BOOLEAN',
133
- hasViolation: !result.passed,
134
- violationCount: result.ironLaws.filter(r => !r.satisfied).length,
135
- };
136
- } catch {
137
- return {
138
- strategy: 'SAFE_BOOLEAN',
139
- hasViolation: true,
140
- violationCount: 1,
141
- };
142
- }
143
- }
144
- }
145
-
146
- /**
147
- * 快捷:BLOCK 策略执行检查
148
- */
149
- export async function executeWithBlock(
150
- checkFn: () => Promise<ConstraintCheckResult>
151
- ): Promise<ViolationHandlingResult> {
152
- return new ConstraintViolationHandler().execute(checkFn, 'BLOCK');
153
- }
154
-
155
- /**
156
- * 快捷:COLLECT 策略执行检查
157
- */
158
- export async function executeWithCollect(
159
- checkFn: () => Promise<ConstraintCheckResult>
160
- ): Promise<ViolationHandlingResult> {
161
- return new ConstraintViolationHandler().execute(checkFn, 'COLLECT');
162
- }
163
-
164
- /**
165
- * 快捷:SAFE_BOOLEAN 策略执行检查
166
- */
167
- export async function executeWithSafeBoolean(
168
- checkFn: () => Promise<ConstraintCheckResult>
169
- ): Promise<ViolationHandlingResult> {
170
- return new ConstraintViolationHandler().execute(checkFn, 'SAFE_BOOLEAN');
171
- }
@@ -1,140 +0,0 @@
1
- /**
2
- * KnowledgeLifecycleHooks 测试
3
- */
4
-
5
- import { KnowledgeLifecycleHooks } from '../lifecycle-hooks';
6
- import { FileKnowledgeStore as KnowledgeStore } from '../store';
7
- import { KnowledgeQuery } from '../query';
8
- import * as fs from 'fs';
9
- import * as path from 'path';
10
- import * as os from 'os';
11
-
12
- describe('KnowledgeLifecycleHooks', () => {
13
- let store: KnowledgeStore;
14
- let query: KnowledgeQuery;
15
- let hooks: KnowledgeLifecycleHooks;
16
- let tmpDir: string;
17
-
18
- beforeEach(() => {
19
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'hooks-test-'));
20
- store = new KnowledgeStore({ baseDir: path.join(tmpDir, 'knowledge') });
21
- query = new KnowledgeQuery(store);
22
- hooks = new KnowledgeLifecycleHooks({ store, query });
23
- });
24
-
25
- afterEach(() => {
26
- fs.rmSync(tmpDir, { recursive: true, force: true });
27
- });
28
-
29
- function saveEntry(overrides: any = {}) {
30
- store.save({
31
- id: overrides.id || `test-${Math.random().toString(36).slice(2, 6)}`,
32
- type: 'decision',
33
- title: 'Test Decision',
34
- content: 'Some content',
35
- maturity: 'verified',
36
- layer: 'project',
37
- created: new Date().toISOString(),
38
- lastReferenced: new Date().toISOString(),
39
- contributors: [],
40
- projects: [],
41
- tags: [],
42
- applicablePhases: [],
43
- sourceReferences: [],
44
- referencedBy: [],
45
- executionResults: [],
46
- consumptionMode: 'reference',
47
- origin: 'agent',
48
- ...overrides,
49
- });
50
- }
51
-
52
- describe('onSessionStart', () => {
53
- it('应该返回空数组当无知识条目', () => {
54
- const sources = hooks.onSessionStart();
55
- expect(sources.length).toBe(0);
56
- });
57
-
58
- it('应该注入知识条目为 ContextSource', () => {
59
- saveEntry({ id: 'dec-001', title: 'Use TypeScript', content: 'Always use TS', tags: ['typescript', 'best-practice'] });
60
- const sources = hooks.onSessionStart({ budget: 8000 });
61
- expect(sources.length).toBe(1);
62
- expect(sources[0].type).toBe('knowledge');
63
- expect(sources[0].priority).toBe(3);
64
- expect(sources[0].content).toContain('Use TypeScript');
65
- expect(sources[0].content).toContain('typescript');
66
- });
67
-
68
- it('应该排除指定条目', () => {
69
- saveEntry({ id: 'ex-1', title: 'Excluded' });
70
- const sources = hooks.onSessionStart({ budget: 8000, exclude: ['ex-1'] });
71
- expect(sources.length).toBe(0);
72
- });
73
- });
74
-
75
- describe('onTaskComplete', () => {
76
- it('应该从错误中提取 pitfall', () => {
77
- const result = hooks.onTaskComplete({
78
- taskDescription: 'Fix login bug',
79
- errors: ['TypeError: Cannot read property of undefined'],
80
- });
81
-
82
- expect(result.entries.length).toBe(1);
83
- expect(result.entries[0].type).toBe('pitfall');
84
- expect(result.entries[0].tags).toContain('auto-extract');
85
- expect(result.source).toBe('archive');
86
- });
87
-
88
- it('应该从决策中提取 decision', () => {
89
- const result = hooks.onTaskComplete({
90
- taskDescription: 'Choose architecture',
91
- decisions: ['使用微服务架构因为需要独立部署'],
92
- });
93
-
94
- expect(result.entries.length).toBe(1);
95
- expect(result.entries[0].type).toBe('decision');
96
- });
97
-
98
- it('应该限制提取数量', () => {
99
- const errors = Array.from({ length: 10 }, (_, i) => `Error ${i}`);
100
- const result = hooks.onTaskComplete({
101
- taskDescription: 'Test',
102
- errors,
103
- });
104
-
105
- expect(result.entries.length).toBeLessThanOrEqual(3);
106
- });
107
-
108
- it('应该处理空上下文', () => {
109
- const result = hooks.onTaskComplete({ taskDescription: 'Empty' });
110
- expect(result.entries.length).toBe(0);
111
- });
112
- });
113
-
114
- describe('onError', () => {
115
- it('应该从错误中提取知识', () => {
116
- const result = hooks.onError(new Error('Connection timeout'), {
117
- taskDescription: 'API call',
118
- stackTrace: 'at line 42',
119
- });
120
-
121
- expect(result.entries.length).toBe(1);
122
- expect(result.entries[0].type).toBe('pitfall');
123
- expect(result.entries[0].content).toContain('Connection timeout');
124
- expect(result.entries[0].content).toContain('API call');
125
- expect(result.source).toBe('error');
126
- });
127
-
128
- it('应该保存到知识库', () => {
129
- hooks.onError(new Error('Test error'));
130
- const entries = store.list({ excludeArchived: false });
131
- expect(entries.length).toBe(1);
132
- expect(entries[0].title).toContain('Test error');
133
- });
134
-
135
- it('应该处理无上下文的错误', () => {
136
- const result = hooks.onError(new Error('Simple error'));
137
- expect(result.entries.length).toBe(1);
138
- });
139
- });
140
- });